package com.jwsoft.manager.core.integration.eduAddressTree.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bifang.assistant.common.vo.BasePageVO;
import com.bifang.assistant.common.vo.BaseVO;
import com.bifang.assistant.common.vo.SessionInfo;
import com.bifang.engine.assistant.core.annotation.ApiService;
import com.bifang.engine.assistant.core.annotation.OpApi;
import com.bifang.engine.assistant.core.enums.AuthStatusEnum;
import com.bifang.engine.assistant.core.enums.BoolEnum;
import com.bifang.engine.assistant.core.enums.FuncTypeEnum;
import com.bifang.engine.assistant.core.exception.AppException;
import com.bifang.engine.assistant.core.utils.SessionUtil;
import com.bifang.engine.data.core.util.PagerUtil;
import com.bifang.engine.protect.core.annotation.NoSignCrypto;
import com.bifang.module.base.common.util.TreeUtil;
import com.bifang.module.base.common.vo.common.CommonVO;
import com.bifang.module.base.common.vo.dict.DictAreaVO;
import com.bifang.module.base.common.vo.dict.SelectVO;
import com.bifang.module.base.common.vo.excel.ExcelModelVO;
import com.bifang.module.base.common.vo.excel.ExcelVO;
import com.bifang.module.base.common.vo.tree.TreeVO;
import com.bifang.module.base.core.helper.DictHelper;
import com.bifang.module.base.core.helper.ExcelCommonHelper;
import com.bifang.module.base.core.integration.CommonIntegration;
import com.bifang.module.base.core.integration.DictIntegration;
import com.bifang.module.base.core.integration.ExcelIntegration;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jwsoft.manager.common.constant.EduCache;
import com.jwsoft.manager.common.enums.*;
import com.jwsoft.manager.common.vo.address.AddressSchoolVO;
import com.jwsoft.manager.common.vo.address.AddressTreeSchoolVO;
import com.jwsoft.manager.common.vo.addressParse.AddressInfo;
import com.jwsoft.manager.common.vo.eduAddressSchoolAudit.EduAddressSchoolAuditVO;
import com.jwsoft.manager.common.vo.eduAddressTree.*;
import com.jwsoft.manager.common.vo.eduAddressTreeInitial.EduAddressTreeInitialQueryVO;
import com.jwsoft.manager.common.vo.eduAddressTreeInitial.EduAddressTreeInitialVO;
import com.jwsoft.manager.common.vo.eduAddressTreeSchool.EduAddressSchoolRelationAddVO;
import com.jwsoft.manager.common.vo.eduAddressTreeSchool.EduAddressTreeSchoolQueryVO;
import com.jwsoft.manager.common.vo.eduAddressTreeSchool.EduAddressTreeSchoolRemoveVO;
import com.jwsoft.manager.common.vo.eduAddressTreeSchool.EduAddressTreeSchoolVO;
import com.jwsoft.manager.common.vo.eduAnalyHj.EduAnalyHjVO;
import com.jwsoft.manager.common.vo.eduSchool.EduSchoolVO;
import com.jwsoft.manager.core.dao.model.*;
import com.jwsoft.manager.core.dao.service.*;
import com.jwsoft.manager.core.integration.EduAddressSchoolIntegration;
import com.jwsoft.manager.core.integration.EduAddressTreeInitialIntegration;
import com.jwsoft.manager.core.integration.addressParse.SmartParse;
import com.jwsoft.manager.core.integration.eduAddressTree.EduAddressTreeIntegration;
import com.jwsoft.manager.core.integration.eduAddressTree.TreeChangeHelper;
import com.jwsoft.manager.core.util.AddressUtil;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 业务实现类
 *
 * @author fangs
 * @since 2023-01-12
 */
@Slf4j
@ApiService(funcCode = "eduAddressTree", title = "业务实现")
public class EduAddressTreeIntegrationImpl implements EduAddressTreeIntegration {

    /**
     * 可以保存到es的节点类型
     */
    private static final Set<String> ES_ENABLE_NODE = new HashSet<>(Arrays.asList("province", "city", "district", "town", "village",
            "road", "detail"));

    /**
     * 不可以从es删除的节点类型
     */
    private static final Set<String> DISABLED_ES_NODE = new HashSet<>(Arrays.asList("province", "city", "district", "town"));


    /**
     * 省和直辖市的后缀名
     */
    private static final List<String> PROVINCE_SUFFIX = Arrays.asList("省", "市", "自治区", "壮族自治区", "维吾尔自治区");

    /**
     * 地市的后缀名
     */
    private static final List<String> CITY_SUFFIX = Arrays.asList("市", "地区", "自治州");

    /**
     * 区县后缀名
     */
    private static final List<String> DISTRICT_SUFFIX = Arrays.asList("县", "市", "区");

    /**
     * 超级管理员权限名
     */
    private static final String SUPER_ADMIN_NAME = "SUPERADMIN";

    private static final String AREA_TYPE = "town";

    private static final Integer MIX_NUM = 10;

    @Autowired
    private EduAddressTreeService eduAddressTreeService;
    @Autowired
    private EduAddressTreeSchoolService eduAddressTreeSchoolService;
    @Autowired
    private DictHelper dictHelper;
    @Autowired
    private AddressMessageService addressMessageService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private EduSchoolService eduSchoolService;
    @Autowired
    private EduAddressSchoolService eduAddressSchoolService;
    @Autowired
    ApplicationContext applicationContext;
    @Autowired
    private EduAreaService eduAreaService;
    @Autowired
    private EduAddressService eduAddressService;
    @Autowired
    public ExcelIntegration excelIntegration;
    @Value("${spring.servlet.multipart.location}")
    private String tempPath;
    @Autowired
    private ExcelCommonHelper excelCommonHelper;
    @Autowired
    private DictIntegration dictIntegration;


    @Autowired
    private EduAddressSchoolAuditService schoolAuditService;

    @Autowired
    @Lazy
    private EduAddressTreeInitialIntegration initialIntegration;
    @Autowired
    private SmartParse smartParse;
    @Autowired
    private CommonIntegration commonIntegration;

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    public EduAddressSchoolIntegration eduAddressSchoolIntegration;

    @PostConstruct
    protected void init() {
        AddressTree.context = applicationContext;
    }


    @OpApi(
            funcCode = "eduAddressTree0001",
            title = "初始化ES地址数据",
            funcType = FuncTypeEnum.insert,
            publicFlag = BoolEnum.TRUE)
    @Override
    public void initAddressTreeEs(BaseVO baseVO) {
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTree::getLastNode, 1)
                .eq(EduAddressTree::getEsStatus, 0);
        List<EduAddressTree> list;
        do {
            PageHelper.startPage(1, 1000);
            list = eduAddressTreeService.list(queryWrapper);
            for (EduAddressTree addressTree : list) {
                try {
                    AddressEsVO addressEsVO = entityToESVO(addressTree);
                    addressMessageService.saveAddressData(addressEsVO);
                    updateEsStatus(addressTree.getAddressId(), 1, "保存成功");
                } catch (Exception e) {
                    log.info("插入错误:{}", addressTree != null ? addressTree.getAddressId() : "eduAddressTree为空");
                    log.error("插入错误" + e.getMessage());
                    if (addressTree != null && addressTree.getAddressId() != null) {
                        updateEsStatus(addressTree.getAddressId(), e);
                    }
                }
            }
        } while (list.size() > 0);
    }

    @OpApi(
            funcCode = "eduAddressTree0002",
            title = "地址格式化",
            funcType = FuncTypeEnum.insert,
            publicFlag = BoolEnum.TRUE)
    @Override
    public AddressVO parseAddressAuto(AddressReqVO addressReqVO) {
        //格式化地址
        AddressRequest addressRequest = new AddressRequest();
        addressRequest.setAddress(addressReqVO.getAddress());
        return addressMessageService.parseAddressAuto(addressRequest);
    }

    @OpApi(
            funcCode = "eduAddressTree0003",
            title = "地址格式化并自动保存到数据库和ES",
            funcType = FuncTypeEnum.insert,
            publicFlag = BoolEnum.TRUE)
    @Override
    public AddressVO parseAddressAutoAndSave(AddressRequest addressRequest) {
        AddressVO addressVO = this.parseAddressAuto(new AddressReqVO(addressRequest.getAddress()));
        AddressUtil.reFormatAddress(addressVO);
        //更新数据
        long addressId = this.saveAddressInfo(addressVO);
        addressVO.setAddressId(addressId);
        return addressVO;
    }


    @OpApi(
            funcCode = "eduAddressTree0004",
            title = "根据parentId获取下级节点",
            funcType = FuncTypeEnum.query)
    @Override
    public PageInfo<AddressTreeVO> getListByParentId(AddressTreeReqVO addressTreeReqVO) {
        if (addressTreeReqVO.getPageSize() == null || addressTreeReqVO.getPageSize() == 0) {
            addressTreeReqVO.setPageSize(20);
        }
        if (addressTreeReqVO.getPageNum() == null) {
            addressTreeReqVO.setPageNum(1);
        }
        String areaCode = SessionUtil.getSessionInfo().getAreaCode();
        PageHelper.startPage(addressTreeReqVO.getPageNum(), addressTreeReqVO.getPageSize());
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .ne(EduAddressTree::getAreaType, "error")
                .eq(EduAddressTree::getMajorPointer, "0");
        if (addressTreeReqVO.getParentId() == null || addressTreeReqVO.getParentId() == 0) {
            if (!StringUtils.hasText(areaCode)) {
                Set<String> systemPrivileges = SessionUtil.getSystemPrivileges();
                if (!systemPrivileges.contains(SUPER_ADMIN_NAME)) {
                    throw new AppException("你的账号或组织没有配置行政区划，请联系管理配置");
                }
                addressTreeReqVO.setParentId(0L);
            } else {
                queryWrapper.lambda().eq(EduAddressTree::getAreaCode, areaCode);
                addressTreeReqVO.setParentId(null);
            }
        }
        queryWrapper.lambda().eq(addressTreeReqVO.getParentId() != null, EduAddressTree::getParentId,
                        addressTreeReqVO.getParentId())
                .orderByAsc(EduAddressTree::getOrderNo, EduAddressTree::getAreaCode, EduAddressTree::getAreaName);
        List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return PageInfo.emptyPageInfo();
        }
        PageInfo<EduAddressTree> pageInfo = new PageInfo<>(list);
        List<AddressTreeVO> addressTreeVOList = new ArrayList<>(list.size());
        for (EduAddressTree eduAddressTree : list) {
            AddressTreeVO addressTreeVO = Convert.convert(AddressTreeVO.class, eduAddressTree);
            addressTreeVO.setMatchNode(eduAddressTree.getEsStatus() == 1);
            // String分割字符串转String数组
            if (eduAddressTree.getPrimarySchool() != null) {
                List<String> primaryList = Arrays.stream(eduAddressTree.getPrimarySchool().split(",")).collect(Collectors.toList());
                addressTreeVO.setCheckPrimaryList(primaryList);
            } else {
                addressTreeVO.setCheckPrimaryList(new ArrayList<>());
            }
            if (eduAddressTree.getMiddleSchool() != null) {
                List<String> middleList = Arrays.stream(eduAddressTree.getMiddleSchool().split(",")).collect(Collectors.toList());
                addressTreeVO.setCheckMiddleList(middleList);
            } else {
                addressTreeVO.setCheckMiddleList(new ArrayList<>());
            }
            addressTreeVOList.add(addressTreeVO);
        }
        dictHelper.valueToName(addressTreeVOList, Arrays.asList(EduAddressTreeDictEnum.values()));
        return PagerUtil.parsePagerVo(addressTreeVOList, pageInfo);
    }

    @OpApi(
            funcCode = "eduAddressTree0005",
            title = "获取地址树节点绑定的学校",
            funcType = FuncTypeEnum.query,
            publicFlag = BoolEnum.TRUE)
    @Override
    public PageInfo<EduAddressTreeSchoolVO> getSchool(EduAddressTreeSchoolQueryVO treeSchoolQueryVO) {
        //设置默认分页参数
        if (treeSchoolQueryVO.getPageSize() == null || treeSchoolQueryVO.getPageSize() == 0) {
            treeSchoolQueryVO.setPageSize(10);
        }
        if (treeSchoolQueryVO.getPageNum() == null) {
            treeSchoolQueryVO.setPageNum(1);
        }

        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressTreeSchool::getTreeId, treeSchoolQueryVO.getTreeId());
        if (StringUtils.hasText(treeSchoolQueryVO.getSchoolCategory())) {
            queryWrapper.lambda().eq(EduAddressTreeSchool::getSchoolCategory, treeSchoolQueryVO.getSchoolCategory());
        }
        if (StringUtils.hasText(treeSchoolQueryVO.getSchoolNature())) {
            queryWrapper.lambda().eq(EduAddressTreeSchool::getSchoolNature, treeSchoolQueryVO.getSchoolNature());
        }
        //分页查询
        PageHelper.startPage(treeSchoolQueryVO.getPageNum(), treeSchoolQueryVO.getPageSize());
        List<EduAddressTreeSchool> list = eduAddressTreeSchoolService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            PagerUtil.createEmptyPagerInfo(treeSchoolQueryVO.getPageNum(), treeSchoolQueryVO.getPageSize());
        }
        PageInfo<EduAddressTreeSchool> pageInfo = new PageInfo<>(list);
        List<EduAddressTreeSchoolVO> treeSchoolVOList = Convert.toList(EduAddressTreeSchoolVO.class, list);

        //查询学校与地址关联表数据
        QueryWrapper<EduAddressSchool> eduAddressSchoolQueryWrapper = new QueryWrapper<>();
        eduAddressSchoolQueryWrapper.lambda()
                .eq(EduAddressSchool::getTreeId,treeSchoolQueryVO.getTreeId());
        List<EduAddressSchool> eduAddressSchools = eduAddressSchoolService.list(eduAddressSchoolQueryWrapper);
        //如果是最下面的结点，他与edu_address_tree表的数据不会为空,设置服务区和原住民表示
        if (!CollectionUtils.isEmpty(eduAddressSchools)) {
            // 创建 schoolId 与 EduAddressSchool 对象的映射
            Map<String, EduAddressSchool> addressSchoolMap = eduAddressSchools.stream()
                    .collect(Collectors.toMap(
                            EduAddressSchool::getSchoolId,
                            Function.identity(),
                            (existing, replacement) -> existing
                    ));// 保留第一个遇到的
            treeSchoolVOList.forEach(item -> {
                // 获取 item 的 schoolId
                String schoolId = item.getSchoolId();
                // 检查 addressSchoolMap 是否包含该 schoolId
                EduAddressSchool addressSchool = addressSchoolMap.get(schoolId);
                if (addressSchool != null) {
                    item.setNativeFlag(addressSchool.getNativeFlag());
                    item.setServiceAreaFlag(addressSchool.getServiceAreaFlag());
                } else {
                    item.setNativeFlag(null);
                    item.setServiceAreaFlag(null);
                }
            });
        }

        //转换字典码
        dictHelper.valueToName(treeSchoolVOList, Arrays.asList(EduAddressTreeSchoolDictEnum.values()));
        return PagerUtil.parsePagerVo(treeSchoolVOList, pageInfo);
    }

    @OpApi(
            funcCode = "eduAddressTree0006",
            title = "地址树节点移动",
            funcType = FuncTypeEnum.update,
            publicFlag = BoolEnum.TRUE)
    @Override
    @Transactional
    public void moveTreeNode(MoveNodeReqVO moveNodeReqVO) {
        RLock rLock = redissonClient.getReadWriteLock(EduCache.EDU_ADDRESS_TREE_NODE_LOCK + moveNodeReqVO.getAddressId()).writeLock();
        boolean res = false;
        try {
            res = rLock.tryLock(5000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("地址树节点移动获取锁等待中断，节点id:{}", moveNodeReqVO.getAddressId(), e);
        }
        if (res) {
            try {
                //节点移动
                EduAddressTree eduAddressTree = eduAddressTreeService.getById(moveNodeReqVO.getAddressId());
                EduAddressTreeVO eduAddressTreeVO = Convert.convert(EduAddressTreeVO.class, eduAddressTree);
                eduAddressTreeVO.setChangeType(DataSaveEnum.update);
                eduAddressTreeVO.setOldParentId(eduAddressTreeVO.getParentId());
                eduAddressTreeVO.setParentId(moveNodeReqVO.getTargetParentId());

                Map<DataSaveEnum, List<EduAddressTreeVO>> saveData = new HashMap<>();
                saveData.put(DataSaveEnum.update, Collections.singletonList(eduAddressTreeVO));
                eduAddressTreeService.applyMerge(saveData, null);
            } finally {
                rLock.unlock();
            }
        } else {
            throw new AppException("当前节点正在使用中，不能进行移动，请稍后重试");
        }
    }

    @Override
    @OpApi(
            funcCode = "eduAddressTree0007",
            title = "根据用户信息获取乡镇街道",
            funcType = FuncTypeEnum.query, publicFlag = BoolEnum.TRUE)
    public List<TreeVO> getTownTreeByUser(BaseVO vo) {
        String areaCode = SessionUtil.getAreaCode();
        if (!StringUtils.hasText(areaCode)) {
            return new ArrayList<>();
        }
        List<TreeVO> treeVOList = new ArrayList<>();
        LambdaQueryWrapper<EduAddressTree> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(EduAddressTree::getAreaCode, areaCode);
        List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        setTown(list.get(0), treeVOList);
        treeVOList = TreeUtil.constructTaskDTOToTree(treeVOList);
        return treeVOList;
    }

    private void setTown(EduAddressTree addressTree, List<TreeVO> treeVOList) {
        Map<String, Object> map = new HashMap<>();
        map.put("title", "title");
        if (addressTree == null) {
            return;
        }
        if ("city".equalsIgnoreCase(addressTree.getAreaType())) {
            LambdaQueryWrapper<EduAddressTree> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(EduAddressTree::getParentId, addressTree.getAddressId())
                    .orderByAsc(EduAddressTree::getOrderNo);
            List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            List<Long> parentIds = new ArrayList<>();
            for (EduAddressTree tree : list) {
                TreeVO antTreeVO = new TreeVO();
                antTreeVO.setKey(tree.getAddressId().toString());
                // 选择后获取的值
                antTreeVO.setValue(tree.getAreaCode());
                antTreeVO.setParentKey(null);
                antTreeVO.setTitle(tree.getAreaName());
                antTreeVO.setScopedSlots(map);
                antTreeVO.setSelectable(false);
                antTreeVO.setDisabled(true);
                antTreeVO.setDisableCheckbox(true);
                treeVOList.add(antTreeVO);
                parentIds.add(tree.getAddressId());
            }
            LambdaQueryWrapper<EduAddressTree> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.in(EduAddressTree::getParentId, parentIds).orderByAsc(EduAddressTree::getOrderNo);
            List<EduAddressTree> list1 = eduAddressTreeService.list(queryWrapper1);
            if (CollectionUtils.isEmpty(list1)) {
                return;
            }
            for (EduAddressTree tree : list1) {
                TreeVO antTreeVO = new TreeVO();
                antTreeVO.setKey(tree.getAddressId().toString());
                // 选择后获取的值
                antTreeVO.setValue(tree.getAreaCode());
                antTreeVO.setParentKey(tree.getParentId().toString());
                antTreeVO.setTitle(tree.getAreaName());
                antTreeVO.setScopedSlots(map);
                treeVOList.add(antTreeVO);
            }
        } else if ("district".equalsIgnoreCase(addressTree.getAreaType())) {
            LambdaQueryWrapper<EduAddressTree> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper
                    .eq(EduAddressTree::getParentId, addressTree.getAddressId())
                    .isNotNull(EduAddressTree::getAreaCode)
                    .orderByAsc(EduAddressTree::getOrderNo);
            List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
            if (CollectionUtils.isEmpty(list)) {
                return;
            }
            for (EduAddressTree tree : list) {
                TreeVO antTreeVO = new TreeVO();
                antTreeVO.setKey(tree.getAddressId().toString());
                // 选择后获取的值
                antTreeVO.setValue(tree.getAreaCode());
                antTreeVO.setParentKey(null);
                antTreeVO.setTitle(tree.getAreaName());
                antTreeVO.setScopedSlots(map);

                treeVOList.add(antTreeVO);
            }
        }
    }

    @OpApi(
            funcCode = "eduAddressTree0008",
            title = "根据主键获取地址详细信息",
            funcType = FuncTypeEnum.query)
    @Override
    public EduAddressTreeInfoVO queryAddressInfoById(AddressTreeQueryVO vo) {
        if (ObjectUtils.isEmpty(vo.getAddressId())) {
            throw new AppException("地址id不能为空");
        }
        EduAddressTree entity = eduAddressTreeService.getById(vo.getAddressId());
        if (ObjectUtils.isEmpty(entity)) {
            return null;
        }
        EduAddressTreeVO convert = Convert.convert(EduAddressTreeVO.class, entity);
        List<String> searchTermList = new ArrayList<>(10);
        dictHelper.valueToName(convert, Arrays.asList(EduAddressTreeDictEnum.values()));
        if (!ObjectUtils.isEmpty(entity.getSearchTerm())) {
            String searchTerm = convert.getSearchTerm();
            Collections.addAll(searchTermList, searchTerm.split(AddressTree.SEARCH_TERM_SEPARATOR));
        }
        EduAddressTreeInfoVO data = new EduAddressTreeInfoVO();
        data.setSearchTermList(searchTermList);
        data.setData(convert);
        return data;
    }

    @OpApi(
            funcCode = "eduAddressTree0009",
            title = "删除地址关联学校信息",
            funcType = FuncTypeEnum.update,
            publicFlag = BoolEnum.TRUE)
    @Override
    @Transactional
    public void removeSchoolRelation(EduAddressTreeSchoolRemoveVO vo) {
        if (vo.getTreeId() == null) {
            throw new AppException("树节点不能为空！");
        }
        if (ObjectUtils.isEmpty(vo) || vo.getIds().size() == 0) {
            throw new AppException("未勾选关联信息!");
        }
        //没有传入，默认都需要向下处理子节点
        if (StringUtils.isEmpty(vo.getHandleChildFlag())) {
            vo.setHandleChildFlag(BoolEnum.TRUE.getType());
        }
        //删除地址关联学校信息，需要向下删除，并且删除edu_address_school 关联数据

        //1.通过treeId 得到下级的所有节点信息，并且找出叶子节点
        List<Long> treeIds = new ArrayList<>();
        if (BoolEnum.TRUE.getType().equals(vo.getHandleChildFlag())) {
            List<EduAddressTree> allDescendants = eduAddressTreeService.getAllDescendants(vo.getTreeId());
            if (!CollectionUtils.isEmpty(allDescendants)) {
                treeIds = allDescendants.stream().map(EduAddressTree::getAddressId).collect(Collectors.toList());
            }
        }
        //1.1得到需要操作的树节点id
        treeIds.add(vo.getTreeId());
        //1.2 得到地址节点 edu_address
        List<EduAddress> addressList = eduAddressTreeService.getAddressByTreeId(treeIds);

        //2.通过ids得到学校id
        List<EduAddressTreeSchool> addressTreeSchoolList = eduAddressTreeSchoolService.listByIds(vo.getIds());
        if (CollectionUtils.isEmpty(addressTreeSchoolList)) {
            return;
        }
        List<String> schoolIds = addressTreeSchoolList.stream().map(EduAddressTreeSchool::getSchoolId).collect(Collectors.toList());

        // 判断 edu_address_tree 表中是否已经锁定，锁定了就不能取消
        EduAddressTree eduAddressTree = eduAddressTreeService.getById(vo.getTreeId());
        List<String> middleList = new ArrayList<>();
        if (eduAddressTree.getMiddleSchool() != null) {
            middleList = Arrays.asList(eduAddressTree.getMiddleSchool().split(","));
        }

        List<String> primaryList = new ArrayList<>();
        if (eduAddressTree.getPrimarySchool() != null) {
            primaryList = Arrays.asList(eduAddressTree.getPrimarySchool().split(","));
        }

        // 如果 schoolIds 中的 ID 在 middleList 存在
        if (eduAddressTree.getAffirmFlag().equals(1) && !middleList.isEmpty() && middleList.containsAll(schoolIds)) {
            throw new AppException("该节点中学已被锁定，不能取消关联！");
        }
        if (eduAddressTree.getAffirmFlag().equals(1) && !primaryList.isEmpty() && primaryList.containsAll(schoolIds)) {
            throw new AppException("该节点小学已被锁定，不能取消关联！");
        }


        //3.将edu_address_school中的数据进行删除
        for (EduAddress eduAddress : addressList) {
            QueryWrapper<EduAddressSchool> addressSchoolQueryWrapper = new QueryWrapper<>();
            addressSchoolQueryWrapper.lambda()
                    .eq(EduAddressSchool::getAddressId, eduAddress.getAddressId())
                    .in(EduAddressSchool::getSchoolId, schoolIds);
            eduAddressSchoolService.remove(addressSchoolQueryWrapper);
        }

        //4.将edu_address_tree_school的数据进行删除
        //4.1本级和下级节点都需要删除学校关联
        QueryWrapper<EduAddressTreeSchool> treeSchoolQueryWrapper = new QueryWrapper<>();
        treeSchoolQueryWrapper.lambda()
                .in(EduAddressTreeSchool::getTreeId, treeIds)
                .in(EduAddressTreeSchool::getSchoolId, schoolIds);
        List<EduAddressTreeSchool> list = eduAddressTreeSchoolService.list(treeSchoolQueryWrapper);
        if (!CollectionUtils.isEmpty(list)) {
            List<Long> ids = list.stream().map(EduAddressTreeSchool::getId).collect(Collectors.toList());
            eduAddressTreeSchoolService.removeByIds(ids);
        }

        //4.1选中的上级节点需要判断是否能删除
        List<EduAddressTree> linkByTreeId = eduAddressTreeService.getLinkByTreeId(vo.getTreeId());
        eduAddressTreeSchoolService.removeSchoolsAndAddressTreeRelation(schoolIds, linkByTreeId);
    }


    @OpApi(
            funcCode = "eduAddressTree0010",
            title = "查询地址能关联的学校信息",
            funcType = FuncTypeEnum.query,
            publicFlag = BoolEnum.TRUE)
    @Override
    public PageInfo<EduSchoolVO> querySchoolRelation(EduAddressTreeSchoolQueryVO vo) {
        if (ObjectUtils.isEmpty(vo.getPageSize()) || vo.getPageSize() == 0) {
            vo.setPageSize(20);
        }
        if (ObjectUtils.isEmpty(vo.getPageNum()) || vo.getPageNum() == 0) {
            vo.setPageNum(1);
        }
        if (ObjectUtils.isEmpty(vo.getTreeId())) {
            throw new AppException("请先在路由树中选择需要操作的节点!");
        }
        EduAddressTree entity = eduAddressTreeService.getById(vo.getTreeId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new AppException("数据有误，请联系管理员!");
        }
        List<String> areaCodeList = new ArrayList<>();
        if (ObjectUtils.isEmpty(vo.getAreaCode())) {
            List<SelectVO> selectVOList = dictIntegration.getDistrictByOrg(new DictAreaVO());
            areaCodeList = selectVOList.stream()
                    .map(SelectVO::getDictKey)
                    .collect(Collectors.toList());
            String sessionAreaCode = SessionUtil.getAreaCode();
            if (!areaCodeList.contains(sessionAreaCode)) {
                areaCodeList.add(sessionAreaCode);
            }
        } else {
            areaCodeList.add(vo.getAreaCode());
        }
        if (CollectionUtils.isEmpty(areaCodeList)) {
            throw new AppException("必须传入行政区划");
        }

        //先从关联表中查询出该地址树id关联的学校信息
        QueryWrapper<EduAddressTreeSchool> treeSchoolQueryWrapper = new QueryWrapper<>();
        treeSchoolQueryWrapper.lambda()
                .eq(EduAddressTreeSchool::getTreeId, vo.getTreeId());
        List<EduAddressTreeSchool> treeSchoolList = eduAddressTreeSchoolService.list(treeSchoolQueryWrapper);
        ArrayList<String> schoolIdList =
                treeSchoolList.stream().map(EduAddressTreeSchool::getSchoolId).distinct()
                        .collect(Collectors.toCollection(ArrayList::new));

        //开启分页
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
        QueryWrapper<EduSchool> schoolQueryWrapper = new QueryWrapper<>();
        schoolQueryWrapper.lambda()
                .eq(!ObjectUtils.isEmpty(vo.getSchoolCategory()), EduSchool::getSchoolCategory, vo.getSchoolCategory())
                .eq(!ObjectUtils.isEmpty(vo.getSchoolNature()), EduSchool::getSchoolNature, vo.getSchoolNature())
                .in(EduSchool::getAreaCode, areaCodeList)
                .like(!ObjectUtils.isEmpty(vo.getSchoolName()), EduSchool::getSchoolName, vo.getSchoolName());

        if (schoolIdList.size() > 0) {
            schoolQueryWrapper.lambda().notIn(EduSchool::getOrgId, schoolIdList);
        }

        List<EduSchool> schoolList = eduSchoolService.list(schoolQueryWrapper);
        if (ObjectUtils.isEmpty(schoolList)) {
            return PageInfo.emptyPageInfo();
        }
        PageInfo<EduSchool> pageInfo = new PageInfo<>(schoolList);
        List<EduSchoolVO> eduSchoolVOS = Convert.toList(EduSchoolVO.class, schoolList);

        dictHelper.valueToName(eduSchoolVOS, Arrays.asList(EduAddressTreeSchoolDictEnum.values()));
        return PagerUtil.parsePagerVo(eduSchoolVOS, pageInfo);
    }

    @OpApi(
            funcCode = "eduAddressTree0011",
            title = "添加学校关联信息",
            funcType = FuncTypeEnum.insert,
            publicFlag = BoolEnum.TRUE)
    @Override
    @Transactional
    public void addSchoolRelation(EduAddressSchoolRelationAddVO vo) {
        //添加学校关联信息,需要向下找到叶子节点地址信息
        if (ObjectUtils.isEmpty(vo.getSchoolList())) {
            throw new AppException("未选择学校关联!");
        }

        if (ObjectUtils.isEmpty(vo.getTreeId())) {
            throw new AppException("未选择树节点id");
        }
        if (org.apache.commons.lang3.StringUtils.isEmpty(vo.getNativeFlag())) {
            vo.setNativeFlag("0");
        }
        if (org.apache.commons.lang3.StringUtils.isEmpty(vo.getServiceAreaFlag())) {
            vo.setServiceAreaFlag("0");
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(vo.getVerificationFlag())) {
            vo.setVerificationFlag("0");
        }
        //添加之前先要判断，数据库是否已经有关联信息了,有就过滤
        List<EduSchoolVO> schoolList = vo.getSchoolList().stream()
                .filter(item -> {
                    QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda()
                            .eq(EduAddressTreeSchool::getTreeId, vo.getTreeId())
                            .eq(EduAddressTreeSchool::getSchoolId, item.getOrgId());
                    if (eduAddressTreeSchoolService.count(queryWrapper) > 0) {
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(schoolList)) {
            return;
        }
        //提取要操作的学校id
        List<String> schoolIds = schoolList.stream().map(EduSchoolVO::getOrgId).collect(Collectors.toList());

        //没有传入，默认都需要向下处理子节点
        if (StringUtils.isEmpty(vo.getHandleChildFlag())) {
            vo.setHandleChildFlag(BoolEnum.TRUE.getType());
        }
        //获取当前节点所有后代节点
        List<Long> treeIds = new ArrayList<>();
        if (BoolEnum.TRUE.getType().equalsIgnoreCase(vo.getHandleChildFlag())) {
            List<EduAddressTree> allDescendants = eduAddressTreeService.getAllDescendants(vo.getTreeId());
            if (!CollectionUtils.isEmpty(allDescendants)) {
                treeIds = allDescendants.stream().map(EduAddressTree::getAddressId).collect(Collectors.toList());
            }
        }
        //将当前节点进行添加
        treeIds.add(vo.getTreeId());
        //得到需要操作的地址数据
        List<EduAddress> addressList = eduAddressTreeService.getAddressByTreeId(treeIds);

        if (!CollectionUtils.isEmpty(addressList)) {
            //将地址和学校进行关联
            List<EduAddressSchool> eduAddressSchools = new ArrayList<>();
            for (EduSchoolVO schoolVO : schoolList) {
                for (EduAddress eduAddress : addressList) {
                    //判断数据库是否有数据
                    QueryWrapper<EduAddressSchool> queryWrapper = new QueryWrapper<>();
                    queryWrapper.lambda()
                            .eq(EduAddressSchool::getAddressId, eduAddress.getAddressId())
                            .eq(EduAddressSchool::getSchoolId, schoolVO.getOrgId());
                    if (eduAddressSchoolService.count(queryWrapper) > 0) {
                        continue;
                    }
                    EduAddressSchool addressSchool = new EduAddressSchool();
                    addressSchool.setAddressId(eduAddress.getAddressId());
                    addressSchool.setAddressName(eduAddress.getAddressName());
                    addressSchool.setSchoolId(schoolVO.getOrgId());
                    addressSchool.setSchoolName(schoolVO.getSchoolName());
                    addressSchool.setSchoolCategory(schoolVO.getSchoolCategory());
                    addressSchool.setAreaCode(schoolVO.getAreaCode());
                    addressSchool.setServiceAreaFlag(vo.getServiceAreaFlag());
                    addressSchool.setNativeFlag(vo.getNativeFlag());
                    addressSchool.setVerificationFlag(vo.getVerificationFlag());
                    addressSchool.setAuthStatus(BoolEnum.TRUE.getType());
                    addressSchool.setAuthRemarks(SessionUtil.getUserName() + ":管理员手动添加学校关联！");
                    addressSchool.setAuthTime(new Date());
                    addressSchool.setTreeId(eduAddress.getAddressTreeId());
                    eduAddressSchools.add(addressSchool);
                }
            }
            if (!CollectionUtils.isEmpty(eduAddressSchools)) {
                //进行保存
                eduAddressSchoolService.saveBatch(eduAddressSchools);
            }
        }

        //向下关联地址树和学校
        List<EduAddressTreeSchool> treeSchoolList = new ArrayList<>();
        for (EduSchoolVO schoolVO : schoolList) {
            for (Long treeId : treeIds) {
                EduAddressTreeSchool treeSchool = new EduAddressTreeSchool();
                treeSchool.setSchoolId(schoolVO.getOrgId());
                treeSchool.setSchoolNature(schoolVO.getSchoolNature());
                treeSchool.setSchoolCategory(schoolVO.getSchoolCategory());
                treeSchool.setSchoolNum(schoolVO.getSchoolNum());
                treeSchool.setSchoolName(schoolVO.getSchoolName());
                treeSchool.setShortName(schoolVO.getShortName());
                treeSchool.setAreaCode(schoolVO.getAreaCode());
                treeSchool.setTreeId(treeId);
                treeSchoolList.add(treeSchool);
            }
        }
        eduAddressTreeSchoolService.saveBatch(treeSchoolList);

        //向上关联
        List<EduAddressTreeSchool> collect = schoolList.stream().map(item -> {
            EduAddressTreeSchool eduAddressTreeSchool = new EduAddressTreeSchool();
            eduAddressTreeSchool.setSchoolId(item.getOrgId());
            eduAddressTreeSchool.setSchoolNature(item.getSchoolNature());
            eduAddressTreeSchool.setSchoolCategory(item.getSchoolCategory());
            eduAddressTreeSchool.setSchoolNum(item.getSchoolNum());
            eduAddressTreeSchool.setSchoolName(item.getSchoolName());
            eduAddressTreeSchool.setShortName(item.getShortName());
            eduAddressTreeSchool.setAreaCode(item.getAreaCode());
            return eduAddressTreeSchool;
        }).collect(Collectors.toList());
        //链式获取树节点id，进行向上保存
        EduAddressTree eduAddressTree = eduAddressTreeService.getById(vo.getTreeId());
        cascadeSaveByTreeId(eduAddressTree, collect, schoolIds);
    }


    @OpApi(
            funcCode = "eduAddressTree0012",
            title = "保存地址树节点详细信息",
            funcType = FuncTypeEnum.update,
            publicFlag = BoolEnum.TRUE)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAddressTreeInfo(EduAddressTreeInfoVO vo) {
        if (ObjectUtils.isEmpty(vo)) {
            throw new AppException("保存的信息不能为空!");
        }
        if (ObjectUtils.isEmpty(vo.getData().getAddressId())) {
            throw new AppException("树节点不能为空！");
        }
        if (vo.getData().getAffirmFlag().equals(0)) {
            if (!CollectionUtils.isEmpty(vo.getData().getCheckMiddleList()) || !CollectionUtils.isEmpty(vo.getData().getCheckPrimaryList())) {
                throw new AppException("当前选择状态为未锁定，不能选择默认学校！");
            }
        }
        if (vo.getData().getAffirmFlag().equals(1)) {
            if (CollectionUtils.isEmpty(vo.getData().getCheckMiddleList()) && CollectionUtils.isEmpty(vo.getData().getCheckPrimaryList())) {
                throw new AppException("当前选择状态为锁定，至少选择一个默认学校！");
            }
        }
        // 检查并处理小学列表
        List<String> checkPrimaryList = vo.getData().getCheckPrimaryList();
        String checkPrimary = null;
        if (checkPrimaryList != null && !checkPrimaryList.isEmpty()) {
            checkPrimary = String.join(",", checkPrimaryList);
        }

        // 检查并处理中学列表
        List<String> checkMiddleList = vo.getData().getCheckMiddleList();
        String checkMiddle = null;
        if (checkMiddleList != null && !checkMiddleList.isEmpty()) {
            checkMiddle = String.join(",", checkMiddleList);
        }

        ArrayList<String> schoolList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(vo.getData().getCheckPrimaryList())){
            schoolList.addAll(vo.getData().getCheckPrimaryList());
        }
        if(!CollectionUtils.isEmpty(vo.getData().getCheckMiddleList())){
            schoolList.addAll(vo.getData().getCheckMiddleList());
        }
        EduAddressTreeVO eduAddressTreeVO = vo.getData();
        EduAddressTree entity = eduAddressTreeService.getById(vo.getData().getAddressId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new AppException("没有该节点信息，请重新选择节点再进行修改！");
        }
        if (!ObjectUtils.isEmpty(vo.getSearchTermList())) {
            List<String> searchTermList = vo.getSearchTermList();
            String searchTerm = org.apache.commons.lang3.StringUtils.join(searchTermList, "#");
            eduAddressTreeVO.setSearchTerm(searchTerm);
        } else {
            eduAddressTreeVO.setSearchTerm(eduAddressTreeVO.getAreaName());
        }

        if (!eduAddressTreeVO.getSearchTerm().equals(entity.getSearchTerm())) {
            entity.setSearchTerm(eduAddressTreeVO.getSearchTerm());
        }
        //修改的字段，进行设置
        entity.setAreaName(eduAddressTreeVO.getAreaName());
        entity.setAreaCode(eduAddressTreeVO.getAreaCode());
        entity.setLeafNode(eduAddressTreeVO.getLeafNode());
        entity.setCellphone(eduAddressTreeVO.getCellphone());
        entity.setAreaType(eduAddressTreeVO.getAreaType());
        entity.setContact(eduAddressTreeVO.getContact());
        entity.setRate(eduAddressTreeVO.getRate());
        entity.setFullAddress(eduAddressTreeVO.getFullAddress());
        entity.setAffirmFlag(eduAddressTreeVO.getAffirmFlag());
        entity.setPrimarySchool(checkPrimary);
        entity.setMiddleSchool(checkMiddle);
        eduAddressTreeService.updateById(entity);

        if (eduAddressTreeVO.getAffirmFlag().equals(1)) {
            List<EduAddressTree> treeLinks = new ArrayList<>();
            lockDescendants(eduAddressTreeVO, schoolList, treeLinks, checkPrimary, checkMiddle);
        }

        if (eduAddressTreeVO.getAffirmFlag().equals(0)) {
            EduAddressTree parent = eduAddressTreeService.getById(entity.getParentId());
            if (parent.getAffirmFlag() != null && parent.getAffirmFlag().equals(1)) {
                throw new AppException("当前节点的父节点状态为锁定，不能解锁");
            }
        }
        // syncES(eduAddressTreeVO);
    }

    /**
     * 处理子节点锁定
     *
     * @param eduAddressTreeVO
     * @param schoolList       学校列表
     * @param checkPrimary     小学
     * @param checkMiddle      初中
     * @param treeLinks        新进来的节点
     */
    public void lockDescendants(EduAddressTreeVO eduAddressTreeVO, List<String> schoolList, List<EduAddressTree> treeLinks, String checkPrimary, String checkMiddle) {
        // 获取该节点下面所有的子节点
        List<EduAddressTree> allDescendants = eduAddressTreeService.getAllDescendants(eduAddressTreeVO.getAddressId());
        if (CollectionUtils.isEmpty(allDescendants)) {
            return; // 如果没有子节点，直接返回
        }

        // 提取所有子节点的 ID
        Set<Long> allTreeIds = allDescendants.stream()
                .map(EduAddressTree::getAddressId)
                .collect(Collectors.toSet());

        // 校验时去除新进来的地址节点
        if (!CollectionUtils.isEmpty(treeLinks)) {
            Set<Long> treeLinkIds = treeLinks.stream()
                    .map(EduAddressTree::getAddressId)
                    .collect(Collectors.toSet());
            allTreeIds.removeAll(treeLinkIds); // 移除新进来的地址节点
        }

        // 校验子节点中学校关联的数量和是否是原住民服务区
        if (!allTreeIds.isEmpty()) {
            validateSchoolsInDescendants(new ArrayList<>(allTreeIds), schoolList);
            validateAddressTypesInDescendants(new ArrayList<>(allTreeIds), schoolList);
        }

        // 校验通过批量更新所有子节点的状态
        List<EduAddressTree> items = eduAddressTreeService.listByIds(new ArrayList<>(allDescendants.stream()
                .map(EduAddressTree::getAddressId)
                .collect(Collectors.toSet())));

        items.forEach(item -> {
            item.setAffirmFlag(eduAddressTreeVO.getAffirmFlag());
            item.setPrimarySchool(checkPrimary);
            item.setMiddleSchool(checkMiddle);
        });

        eduAddressTreeService.updateBatchById(items);
    }


    /**
     * 校验子节点中学校关联的数量
     */
    private void validateSchoolsInDescendants(List<Long> treeIds, List<String> schoolList) {
        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().in(EduAddressTreeSchool::getTreeId, treeIds)
                .in(EduAddressTreeSchool::getSchoolId, schoolList);

        long totalCount = eduAddressTreeSchoolService.count(queryWrapper);
        int expectedCount = treeIds.size() * schoolList.size();
        if (totalCount != expectedCount) {
            throw new AppException("子节点中存在没有关联选择学校的节点，不能锁定");
        }
    }

    /**
     * 校验地址类型
     */
    private void validateAddressTypesInDescendants(List<Long> treeIds, List<String> schoolList) {
        List<String> addressIdList = eduAddressTreeService.getAddressByTreeId(treeIds).stream()
                .map(EduAddress::getAddressId)
                .distinct()
                .collect(Collectors.toList());

        // 校验原住民地址
        QueryWrapper<EduAddressSchool> eduAddressSchoolQueryWrapper = new QueryWrapper<>();
        eduAddressSchoolQueryWrapper.lambda()
                .in(EduAddressSchool::getAddressId, addressIdList)
                .in(EduAddressSchool::getSchoolId, schoolList)
                .select(EduAddressSchool::getNativeFlag);
        List<EduAddressSchool> list = eduAddressSchoolService.list(eduAddressSchoolQueryWrapper);

        // 判断是否存在 NativeFlag 为 1 的记录
        boolean hasNativeFlagOne = list.stream()
                .anyMatch(eduAddressSchool -> "1".equals(eduAddressSchool.getNativeFlag()));

        if (hasNativeFlagOne) {
            throw new AppException("存在原住居民地址，不能锁定");
        }


        // 校验服务区地址
        QueryWrapper<EduAddressSchool> eduAddressSchoolQueryWrapper2 = new QueryWrapper<>();
        eduAddressSchoolQueryWrapper2.lambda()
                .in(EduAddressSchool::getAddressId, addressIdList)
                .in(EduAddressSchool::getSchoolId, schoolList)
                .select(EduAddressSchool::getServiceAreaFlag);
        List<EduAddressSchool> list1 = eduAddressSchoolService.list(eduAddressSchoolQueryWrapper2);

        // 判断是否存在 ServiceAreaFlag 为 1 的记录
        boolean hasServiceAreaFlagOne = list1.stream()
                .anyMatch(eduAddressSchool -> "1".equals(eduAddressSchool.getServiceAreaFlag()));

        if (hasServiceAreaFlagOne) {
            throw new AppException("存在服务区地址，不能锁定");
        }

    }

    @OpApi(
            funcCode = "eduAddressTree0013",
            title = "地址树节点新增",
            funcType = FuncTypeEnum.insert,
            publicFlag = BoolEnum.TRUE)
    @Override
    @Transactional
    public EduAddressTreeVO addAddressTreeNode(EduAddressTreeInfoVO vo) {
        if (ObjectUtils.isEmpty(vo)) {
            throw new AppException("请求参数不能为空!");
        }
        EduAddressTreeVO eduAddressTreeVO = vo.getData();
        if (ObjectUtils.isEmpty(eduAddressTreeVO)) {
            throw new AppException("表单信息不能为空!");
        }
        if (ObjectUtils.isEmpty(eduAddressTreeVO.getLeafNode())) {
            throw new AppException("是否为叶子节点不能为空");
        }
        List<String> searchTermList = vo.getSearchTermList();
        if (searchTermList != null && searchTermList.size() > 0) {
            String searchTerm = org.apache.commons.lang3.StringUtils.join(searchTermList, "#");
            eduAddressTreeVO.setSearchTerm(searchTerm);
        }
        EduAddressTree addressTree = Convert.convert(EduAddressTree.class, eduAddressTreeVO);
        addressTree.setMajorPointer(0L);
        addressTree.setEsStatus(0);
        addressTree.setLastNode(0);

        eduAddressTreeVO.setChangeType(DataSaveEnum.insert);
        TreeChangeHelper.addTreeChange(eduAddressTreeVO);
        eduAddressTreeService.saveNewNode(addressTree);
        return Convert.convert(EduAddressTreeVO.class, addressTree);

    }

    @OpApi(
            funcCode = "eduAddressTree0014",
            title = "根据主键删除地址树节点",
            funcType = FuncTypeEnum.delete)
    @Override
    @Transactional
    public void deleteByAddressId(EduAddressTreeKeyVO vo) {
        if (ObjectUtils.isEmpty(vo.getAddressId())) {
            throw new AppException("请求参数不能为空！");
        }
//        LambdaQueryWrapper<EduAddressTree> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(EduAddressTree::getParentId, vo.getAddressId())
//                .eq(EduAddressTree::getMajorPointer, 0);
//        if (eduAddressTreeService.count(queryWrapper) > 0) {
//            throw new AppException("要删除的节点下有子节点，请先删除其子节点");
//        }
        AddressTreeBatchKeyVO batchKeyVO = new AddressTreeBatchKeyVO();
        List<Long> ids = new ArrayList<>();
        ids.add(vo.getAddressId());
        batchKeyVO.setIds(ids);
        removeBatchById(batchKeyVO);
    }

    @OpApi(
            funcCode = "eduAddressTree0015",
            title = "从多个节点名字中抽取共同字段为新的父节点",
            funcType = FuncTypeEnum.query)
    @Override
    public AddressTreeVO mergeNodeAndCreParent(AddressTreeMergeVO vo) {
        return eduAddressTreeService.mergeNodeAndCreateParent(vo.getNodes());
    }

    @OpApi(
            funcCode = "eduAddressTree0016",
            title = "根据主键修改叶子节点",
            funcType = FuncTypeEnum.update)
    @Override
    @Transactional
    public void editLeafNodeByAddressId(EduAddressTreeKeyVO vo) {
        if (ObjectUtils.isEmpty(vo)) {
            throw new AppException("请求参数不能为空！");
        }
        if (ObjectUtils.isEmpty(vo.getAddressId())) {
            throw new AppException("请求参数主键不能为空！");
        }
        EduAddressTree addressTree = eduAddressTreeService.getById(vo.getAddressId());
        if (ObjectUtils.isEmpty(addressTree)) {
            throw new AppException("要修改的节点不存在，请检查后重试！");
        }
        UpdateWrapper<EduAddressTree> wrapper = new UpdateWrapper<>();
        wrapper.lambda().eq(EduAddressTree::getAddressId, vo.getAddressId()).set(EduAddressTree::getLeafNode, 0);
        eduAddressTreeService.update(wrapper);

    }


    @OpApi(
            funcCode = "eduAddressTree0017",
            title = "节点合并",
            funcType = FuncTypeEnum.query)
    @Override
    public AddressTreeVO mergeNode(AddressTreeMergeVO vo) {
        List<AddressTreeVO> nodes = vo.getNodes();
        List<AddressTreeVO> addressTrees = Convert.toList(AddressTreeVO.class, nodes);
        return eduAddressTreeService.mergeNode(addressTrees, vo.getMasterIndex());
    }

    @OpApi(
            funcCode = "eduAddressTree0018",
            title = "地址识别并匹配学区",
            funcType = FuncTypeEnum.query)
    @Override
    public AddressAndSchoolVO parseAddressAndMatchSchool(AddressReqVO addressReqVO) {
        String address = addressReqVO.getAddress();
        if (ObjectUtils.isEmpty(address)) {
            throw new AppException("请输入需要查询的地址");
        }
        //格式化地址
        AddressRequest addressRequest = new AddressRequest();
        addressRequest.setAddress(address);
        AddressVO addressVO = addressMessageService.parseAddressAuto(addressRequest);
        SchoolCategoryEnum[] values = SchoolCategoryEnum.values();
        Map<String, List<EduSchoolVO>> matchSchools = new HashMap<>((int) (values.length / 0.75D + 1));
        for (SchoolCategoryEnum value : values) {
            List<EduSchoolVO> eduSchoolVOS = eduAddressTreeSchoolService.matchSchool(value.getType(), addressVO);
            matchSchools.put(value.getType(), eduSchoolVOS);
        }
        AddressAndSchoolVO addressAndSchoolVO = Convert.convert(AddressAndSchoolVO.class, addressVO);
        addressAndSchoolVO.setMatchSchools(matchSchools);
        return addressAndSchoolVO;
    }

    @OpApi(
            funcCode = "eduAddressTree0019",
            title = "节点合并应用接口，将合并树节点的数据修改在数据库执行",
            funcType = FuncTypeEnum.edit)
    @Transactional
    @Override
    public Map<Long, Long> applyMarge(AddressTreeApplyVO addressTreeApplyVO) {
        if (CollectionUtils.isEmpty(addressTreeApplyVO.getSaveDataList())) {
            throw new AppException("应用列表不能为空");
        }
        //合并节点数据更新元数据
        List<EduAddressTreeVO> saveDataList = new ArrayList<>();
        for (Map<DataSaveEnum, List<EduAddressTreeVO>> dataSaveEnumListMap : addressTreeApplyVO.getSaveDataList()) {
            for (Map.Entry<DataSaveEnum, List<EduAddressTreeVO>> entry : dataSaveEnumListMap.entrySet()) {
                List<EduAddressTreeVO> value = entry.getValue();
                if (CollectionUtils.isEmpty(value)) {
                    continue;
                }
                for (EduAddressTreeVO addressTreeVO : value) {
                    addressTreeVO.setChangeType(entry.getKey());
                    saveDataList.add(addressTreeVO);
                }
            }
        }
        Map<DataSaveEnum, List<EduAddressTreeVO>> changeTypeMap = saveDataList.stream().collect(Collectors.groupingBy(EduAddressTreeVO::getChangeType));
        List<EduAddressTreeVO> updateList = changeTypeMap.get(DataSaveEnum.update);
        List<EduAddressTreeVO> insertList = changeTypeMap.get(DataSaveEnum.insert);
        List<EduAddressTreeVO> deleteList = changeTypeMap.get(DataSaveEnum.delete);
        Map<Long, EduAddressTreeVO> deleteMap;
        if (CollectionUtils.isEmpty(deleteList)) {
            deleteMap = new HashMap<>(0);
        } else {
            deleteMap = deleteList.stream().collect(Collectors.toMap(EduAddressTreeVO::getAddressId, a -> a, (k1, k2) -> k2));
        }
        //同一个节点可能会被多次修改，所以需要去重，保留最后一次修改的数据
        Map<Long, EduAddressTreeVO> updateMap;
        if (CollectionUtils.isEmpty(updateList)) {
            updateMap = new HashMap<>(0);
        } else {
            updateMap = updateList.stream().collect(Collectors.toMap(EduAddressTreeVO::getAddressId, a -> a, (k1, k2) -> k2));
        }
        Map<Long, EduAddressTreeVO> insertMap;
        if (!CollectionUtils.isEmpty(insertList)) {
            //若更新节点中包含插入节点，则将更新的数据提前到插入操作中
            insertMap = new HashMap<>(insertList.size());
            for (EduAddressTreeVO addressTreeVO : insertList) {
                if (updateMap.containsKey(addressTreeVO.getAddressId())) {
                    insertMap.put(addressTreeVO.getAddressId(), updateMap.get(addressTreeVO.getAddressId()));
                    updateMap.remove(addressTreeVO.getAddressId());
                } else {
                    insertMap.put(addressTreeVO.getAddressId(), addressTreeVO);
                }
            }
        } else {
            insertMap = new HashMap<>(0);
        }
        //若更新节点中包含删除节点，则不用更新，也不用插入
        if (!CollectionUtils.isEmpty(deleteMap)) {
            Iterator<Map.Entry<Long, EduAddressTreeVO>> iterator = deleteMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Long, EduAddressTreeVO> next = iterator.next();
                //若更新节点中包含删除节点，则不用更新
                updateMap.remove(next.getKey());
                //若插入节点中包含删除节点，则不用插入,也不用删除
                if (insertMap.containsKey(next.getKey())) {
                    insertMap.remove(next.getKey());
                    iterator.remove();
                }
            }
        }

        Map<Long, Long> idMap = new HashMap<>();
        //更新节点
        Map<DataSaveEnum, List<EduAddressTreeVO>> saveData = new HashMap<>((int) (3 / 0.75D + 1));
        saveData.put(DataSaveEnum.update, new ArrayList<>(updateMap.values()));
        saveData.put(DataSaveEnum.insert, new ArrayList<>(insertMap.values()));
        saveData.put(DataSaveEnum.delete, new ArrayList<>(deleteMap.values()));
        eduAddressTreeService.applyMerge(saveData, idMap);

        //检查更新数据中是否有用于地址解析的节点，有则更新es
        updateList = new ArrayList<>(updateMap.values());
        this.syncES(updateList);
        return idMap;
    }

    @OpApi(
            funcCode = "eduAddressTree0020",
            title = "分页获取学校账号的树节点信息",
            publicFlag = BoolEnum.TRUE,
            funcType = FuncTypeEnum.query)
    @Override
    public PageInfo<AddressTreeForSchoolVO> getSchoolAddressTree(AddressTreeSchoolVO vo) {
        if (vo.getPageSize() == null || vo.getPageSize() == 0) {
            vo.setPageSize(20);
        }
        if (vo.getPageNum() == null) {
            vo.setPageNum(1);
        }
        if (ObjectUtils.isEmpty(vo)) {
            throw new AppException("请求参数不能为空！");
        }
        //从缓存中取出学校账号信息，OrgId  =  school_id
        SessionInfo sessionInfo = SessionUtil.getSessionInfo();
        if (ObjectUtils.isEmpty(sessionInfo)) {
            throw new AppException("登录已过期，请重新登录后重试！");
        }
        if (ObjectUtils.isEmpty(sessionInfo.getOrgId())) {
            throw new AppException("该账号未绑定学校信息，请重试");
        }
        String orgId = sessionInfo.getOrgId();
        if (ObjectUtils.isEmpty(vo.getParentAreaType())) {
            throw new AppException("请求参数不能为空！");
        }
        if ("district".equals(vo.getParentAreaType())) {
            //表示是查找县级下的街道信息，需要显示关联的街道
            List<Long> treeRelationIds = getSchoolAddressTreeRelation();
            if (treeRelationIds.size() == 0) {
                return PageInfo.emptyPageInfo();
            }
            PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
            QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                    .ne(EduAddressTree::getAreaType, "error")
                    .eq(EduAddressTree::getMajorPointer, "0")
                    .in(EduAddressTree::getAddressId, treeRelationIds)
                    .eq(EduAddressTree::getParentId, vo.getParentId());
            List<EduAddressTree> eduAddressTreeList = eduAddressTreeService.list(queryWrapper);
            if (CollectionUtils.isEmpty(eduAddressTreeList)) {
                return PageInfo.emptyPageInfo();
            }
            PageInfo<EduAddressTree> pageInfo = new PageInfo<>(eduAddressTreeList);
            List<AddressTreeForSchoolVO> addressTreeVOList = Convert.toList(AddressTreeForSchoolVO.class, eduAddressTreeList);
            QueryWrapper<EduAddressTreeSchool> wapper = new QueryWrapper<>();
            addressTreeVOList = addressTreeVOList.stream().peek(item -> {
                if (item.getAddressId() == null) {
                    wapper.lambda()
                            .eq(EduAddressTreeSchool::getSchoolId, orgId)
                            .eq(EduAddressTreeSchool::getTreeId, item.getAddressId());
                    EduAddressTreeSchool treeSchool = eduAddressTreeSchoolService.getOne(wapper);
                    if (treeSchool != null) {
                        item.setBoundLeafNum(treeSchool.getBoundLeafNum());
                    } else {
                        item.setBoundLeafNum(0);
                    }
                } else {
                    item.setBoundLeafNum(0);
                }

                wapper.clear();
            }).collect(Collectors.toList());
            dictHelper.valueToName(addressTreeVOList, Arrays.asList(EduAddressTreeDictEnum.values()));
            return PagerUtil.parsePagerVo(addressTreeVOList, pageInfo);
        } else {
            AddressTreeReqVO reqVO = new AddressTreeReqVO();
            BeanUtils.copyProperties(vo, reqVO);
            //调用 04方法，进行复用
            PageInfo<AddressTreeForSchoolVO> pageInfo = getSchoolListByParentId(reqVO);
            if (pageInfo != null) {
                return pageInfo;
            }
            //得到town以下的数据进行处理
            List<AddressTreeForSchoolVO> list = pageInfo.getList();
            list = list.stream().peek(item -> {
                if (item.getLeafNode() == 1) {
                    //应该去eduAddressSchool表中去查询是否有关联叶子节点的数据
                    Long addressId = item.getAddressId();
                    if (addressId != null) {
                        QueryWrapper<EduAddressSchool> queryWrapper = new QueryWrapper<>();
                        queryWrapper.lambda()
                                .eq(EduAddressSchool::getTreeId, addressId)
                                .eq(EduAddressSchool::getSchoolId, orgId);
                        long count = eduAddressSchoolService.count(queryWrapper);
                        if (count > 0) {
                            item.setRelation(true);
                        }
                    }

                }
            }).collect(Collectors.toList());
            pageInfo.setList(list);
            return pageInfo;
        }
    }

    @OpApi(
            funcCode = "eduAddressTree0021",
            title = "学校用户关联树节点接口",
            publicFlag = BoolEnum.TRUE,
            funcType = FuncTypeEnum.update)
    @Override
    @Transactional
    public void schoolRelationAddress(AddressSchoolVO vo) {
        String schoolId = SessionUtil.getSessionInfo().getOrgId();
        if (ObjectUtils.isEmpty(vo)) {
            throw new AppException("请求参数不能为空！");
        }

        List<AddressTreeVO> checkNodes = vo.getCheckNodes();
        if (!CollectionUtils.isEmpty(checkNodes)) {
            //将需要关联的地址信息取出
            List<String> relationIds = getLeafAddress(checkNodes);
            //将学校与节点进行关联
            for (String relationId : relationIds) {
                EduAddressSchoolAuditVO auditVO = new EduAddressSchoolAuditVO();
                auditVO.setAddressId(relationId);
                auditVO.setSchoolId(schoolId);
                auditVO.setId(null);
                eduAddressSchoolService.boundSchoolAndAddress(auditVO);
            }
        }

        List<AddressTreeVO> cancelCheckNodes = vo.getCancelCheckNodes();
        if (cancelCheckNodes.size() > 0) {
            List<String> cancelRelationIds = getLeafAddress(cancelCheckNodes);
            //将学校与节点取消关联 eduAddressTreeSchool
            for (String cancelRelationId : cancelRelationIds) {
                eduAddressSchoolService.unboundSchoolAndAddress(cancelRelationId, schoolId);
            }
        }


    }


    private List<String> getLeafAddress(List<AddressTreeVO> nodes) {
        List<String> relationIds = new ArrayList<>();
        for (AddressTreeVO addressTreeVO : nodes) {
            List<String> leafAddress = this.getLeafAddress(addressTreeVO);
            relationIds.addAll(leafAddress);
        }
        return relationIds;
    }


    private List<String> getLeafAddress(AddressTreeVO addressTreeVO) {
        List<String> relationIds = new ArrayList<>();
        //从不是末级节点，继续往下查询
        if (addressTreeVO.getLastNode() == 0) {
            //查询出该节点的子级节点
            List<EduAddressTree> childrenById = eduAddressTreeService.getChildrenById(addressTreeVO.getAddressId());
            List<AddressTreeVO> addressTreeVOS = Convert.toList(AddressTreeVO.class, childrenById);
            for (AddressTreeVO treeVO : addressTreeVOS) {
                List<String> addressIds = getLeafAddress(treeVO);
                relationIds.addAll(addressIds);
            }
        }
        //如果是叶子节点，去查询出对应的地址id
        if (addressTreeVO.getLeafNode() == 1) {
            List<EduAddress> addressList = eduAddressService.getAddressByTreeId(addressTreeVO.getAddressId());
            if (!CollectionUtils.isEmpty(addressList)) {
                List<String> ids = addressList.stream().map(EduAddress::getAddressId).collect(Collectors.toList());
                relationIds.addAll(ids);
            }
        }
        return relationIds;
    }

    /**
     * 从缓存中，得到orgId，查询 edu_address_tree_school  得到学校被关联树节点的集合
     * 查找县级下的街道信息，需要显示关联的街道
     */
    private List<Long> getSchoolAddressTreeRelation() {
        SessionInfo sessionInfo = SessionUtil.getSessionInfo();
        if (ObjectUtils.isEmpty(sessionInfo)) {
            throw new AppException("登录已过期，请重新登录后重试！");
        }
        if (ObjectUtils.isEmpty(sessionInfo.getOrgId())) {
            throw new AppException("该账号未绑定学校信息，请重试");
        }
        String orgId = sessionInfo.getOrgId();
        //得到学校可以显示的 Town  信息
        List<Long> treeIds = eduAddressTreeSchoolService.getSchoolInToWnList(orgId);

        //得到学校绑定街道信息，默认显示
        EduSchoolVO schoolVO = eduSchoolService.getDetail(orgId);
        if (!ObjectUtils.isEmpty(schoolVO) && !ObjectUtils.isEmpty(schoolVO.getStreet())) {
            QueryWrapper<EduArea> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(EduArea::getLocalCode, schoolVO.getStreet());
            EduArea eduArea = eduAreaService.getOne(queryWrapper);
            String areaCode = eduArea.getAreaCode();
            if (!ObjectUtils.isEmpty(areaCode)) {
                EduAddressTree addressTree = eduAddressTreeService
                        .getOne(new QueryWrapper<EduAddressTree>().lambda().eq(EduAddressTree::getAreaCode, areaCode));
                if (addressTree != null) {
                    treeIds.add(addressTree.getAddressId());
                }

            }
        }
        return treeIds;
    }


    @OpApi(
            funcCode = "eduAddressTree0022",
            title = "节点开启或关闭es同步/节点是否用于地址解析",
            funcType = FuncTypeEnum.edit)
    @Override
    public void syncAddressTreeEs(AddressTreeSyncVO vo) {
        EduAddressTree entity = eduAddressTreeService.getById(vo.getAddressId());
        if (ObjectUtils.isEmpty(entity)) {
            throw new AppException("节点不存在");
        }
        if (vo.getEsStatus()) {
            if (!ES_ENABLE_NODE.contains(entity.getAreaType())) {
                throw new AppException("该节点不支持用于地址解析");
            }
            if (entity.getEsStatus() == 1) {
                throw new AppException("该节点已经用于地址解析");
            }

            AddressEsVO addressEsVO = entityToESVO(entity);
            addressMessageService.saveAddressData(addressEsVO);
            UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(EduAddressTree::getEsStatus, true).eq(EduAddressTree::getAddressId, vo.getAddressId())
                    .set(EduAddressTree::getEsTime, LocalDateTime.now());
            eduAddressTreeService.update(updateWrapper);
        } else {
            if (DISABLED_ES_NODE.contains(entity.getAreaType())) {
                throw new AppException("该节点不能取消用于地址解析");
            }
            if (entity.getEsStatus() == 1) {
                QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(EduAddressTree::getParentId, entity.getAddressId())
                        .eq(EduAddressTree::getEsStatus, true);
                if (eduAddressTreeService.count(queryWrapper) > 0) {
                    throw new AppException("该节点下有已同步的子节点，不能取消同步");
                }
                addressMessageService.deleteAddressByNode(entity.getAreaType(), entity.getAddressId());
                UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
                updateWrapper.lambda().set(EduAddressTree::getEsStatus, false).eq(EduAddressTree::getAddressId, vo.getAddressId())
                        .set(EduAddressTree::getEsTime, LocalDateTime.now());
                eduAddressTreeService.update(updateWrapper);
            } else {
                throw new AppException("该节点未同步,不能取消同步");
            }
        }
    }

    @Override
    public AddressVO nodeToAddressVO(AddressTreeVO addressTreeVO) {
        EduAddressTree entity = Convert.convert(EduAddressTree.class, addressTreeVO);
        return entityToESVO(entity);
    }

    @OpApi(
            funcCode = "eduAddressTree0023",
            title = "重新统计叶子节点数",
            funcType = FuncTypeEnum.edit)
    @Override
    @Async
    public void startEvaluateLeafNum(EduAddressTreeVO eduAddressTreeVO) {
        RLock lock = redissonClient.getLock("startEvaluateLeafNum");
        if (lock.tryLock()) {
            try {
                EduAddressTree entity = eduAddressTreeService.getById(eduAddressTreeVO.getAddressId());
                if (entity == null) {
                    throw new AppException("节点不存在");
                }
                AddressTree addressTree = AddressTree.getAddressTree(entity);
                addressTree.syncLeafNum();
                addressTree.syncBoundSchoolNum();
            } finally {
                lock.unlock();
            }
        }
    }

    @OpApi(
            funcCode = "eduAddressTree0024",
            title = "检查叶子节点数量是否统计完成",
            funcType = FuncTypeEnum.query)
    @Override
    public Boolean isFinishEvaluateLeafNum(BaseVO baseVO) {
        RLock lock = redissonClient.getLock("startEvaluateLeafNum");
        if (lock.tryLock()) {
            lock.unlock();
            return true;
        } else {
            return false;
        }
    }

    @Transactional
    @OpApi(
            funcCode = "eduAddressTree0025",
            title = "根据节点id删除，被删除节点的子节点也会被删除",
            funcType = FuncTypeEnum.delete)
    @Override
    public void removeBatchById(AddressTreeBatchKeyVO keyVO) {
        if (ObjectUtils.isEmpty(keyVO)) {
            throw new AppException("删除id集合为空");
        }
        //查询即将删除的节点信息
        LambdaQueryWrapper<EduAddressTree> treeQw = new LambdaQueryWrapper<>();
        treeQw.in(EduAddressTree::getAddressId, keyVO.getIds());
        List<EduAddressTree> changeList = eduAddressTreeService.list(treeQw);
        if (ObjectUtils.isEmpty(changeList)) {
            throw new AppException("需要删除的节点不存在");
        }

        //查询需要删除节点的所有子节点
        LambdaQueryWrapper<EduAddressTree> queryWrapper = new LambdaQueryWrapper<>();
        List<Long> queryParam = keyVO.getIds();
        //通过循环获取树节点下的所有子节点
        do {
            queryWrapper.select(EduAddressTree::getAddressId)
                    .in(EduAddressTree::getParentId, queryParam);
            List<EduAddressTree> list = eduAddressTreeService.list(queryWrapper);
            if (ObjectUtils.isEmpty(list)) {
                break;
            } else {
                queryParam = list.stream().map(EduAddressTree::getAddressId).collect(Collectors.toList());
                //所有需要删除的节点都添加到了集合中
                keyVO.getIds().addAll(queryParam);
            }
            queryWrapper.clear();
        } while (true);
        //创建set集合进行去重
        Set<Long> idSet = new HashSet<>(keyVO.getIds());
        //删除节点
        eduAddressTreeService.removeBatchByIds(idSet);
        //删除地址树节点-学校 关联
        eduAddressTreeSchoolService.removeByTreeId(idSet);
        //检查审核表中是否有数据，有数据就删除
        QueryWrapper<EduAddress> addressWrapper = new QueryWrapper<>();
        addressWrapper.lambda().in(EduAddress::getAddressTreeId, idSet);
        List<EduAddress> addressList = eduAddressService.list(addressWrapper);
        if (!CollectionUtils.isEmpty(addressList)) {
            List<String> addressId = addressList.stream().map(EduAddress::getAddressId).collect(Collectors.toList());
            QueryWrapper<EduAddressSchoolAudit> wrapper = new QueryWrapper<>();
            wrapper.lambda().in(EduAddressSchoolAudit::getAddressId, addressId);
            List<EduAddressSchoolAudit> auditList = schoolAuditService.list(wrapper);
            if (!CollectionUtils.isEmpty(auditList)) {
                //有数据就删除
                schoolAuditService.removeByIds(auditList.stream().map(EduAddressSchoolAudit::getId).collect(Collectors.toList()));
            }

            //将edu_address中的treeId置空，并且将状态置为未处理
            UpdateWrapper<EduAddress> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().in(EduAddress::getAddressId, addressId)
                    .set(EduAddress::getHandleStatus, HandleStatusEnum.NODO.getType())
                    .set(EduAddress::getHandleMessage, null)
                    .set(EduAddress::getHandleTime, null)
                    .set(EduAddress::getMatchingStatus, MatchingStatusEnum.NODO.getType())
                    .set(EduAddress::getMatchingTime, null)
                    .set(EduAddress::getMatchingMessage, null)
                    .set(EduAddress::getAddressTreeId, null);
            eduAddressService.update(updateWrapper);
        }


        //设置TreeChangeHelper,修正leafNum
//        List<EduAddressTreeVO> changeTreeList = Convert.toList(EduAddressTreeVO.class, changeList);
//        for (EduAddressTreeVO eduAddressTreeVO : changeTreeList) {
//            eduAddressTreeVO.setChangeType(DataSaveEnum.delete);
//        }
//        TreeChangeHelper.addTreeChange(changeTreeList);
    }

    @OpApi(
            funcCode = "eduAddressTree0026",
            title = "学校绑定预警信息",
            funcType = FuncTypeEnum.query)
    @Override
    public PageInfo<AddressBoundCheckVO> queryBoundLessAsLeafNodes(BasePageVO vo) {
        if (vo.getPageSize() == null || vo.getPageSize() == 0) {
            vo.setPageSize(20);
        }
        if (vo.getPageNum() == null) {
            vo.setPageNum(1);
        }
        List<AddressBoundCheckVO> result = eduAddressTreeService.queryBoundLessAsLeafNodes(AREA_TYPE, MIX_NUM, null, null);
        //找到AREA_TYPE下面符合要求的节点，缩小查找范围
        ArrayList<AddressBoundCheckVO> list = new ArrayList<>();
        ArrayList<AddressBoundCheckVO> resultList = new ArrayList<>();

        for (AddressBoundCheckVO item : result) {
            List<AddressBoundCheckVO> nodes
                    = eduAddressTreeService.queryBoundLessAsLeafNodes(null, MIX_NUM, item.getTreeId(), item.getSchoolId());
            if (!CollectionUtils.isEmpty(nodes)) {
                list.addAll(nodes);
            }
        }
        List<AddressBoundCheckVO> targetList = takeOutBoundLeftNode(list);

        QueryWrapper<EduAddressSchool> queryWrapper = new QueryWrapper<>();
        for (AddressBoundCheckVO item : targetList) {
            queryWrapper.lambda()
                    .eq(EduAddressSchool::getTreeId, item.getTreeId())
                    .eq(EduAddressSchool::getSchoolId, item.getSchoolId());
            long count = eduAddressSchoolService.count(queryWrapper);
            if (count > 0) {
                List<EduAddressSchool> eduAddressSchools = eduAddressSchoolService.list(queryWrapper);
                for (EduAddressSchool eduAddressSchool : eduAddressSchools) {
                    item.setAddressName(eduAddressSchool.getAddressName());
                    resultList.add(item);
                }
            }
            queryWrapper.clear();
        }

        return new PageInfo<>(resultList);
    }

    @Override
    @OpApi(
            funcCode = "eduAddressTree0027",
            title = "学校绑定预警信息exec导出",
            funcType = FuncTypeEnum.other)
    @NoSignCrypto
    public void downloadBoundWarningInfo(AddressBoundCheckVO vo, HttpServletResponse response) {
        ExcelVO excelVO = new ExcelVO();
        excelVO.setModelCode("boundWarningInfo");
        ExcelModelVO excelModelVO = excelIntegration.getModel(excelVO);
        String destFilePath = this.tempPath + "/" + IdUtil.fastSimpleUUID() + "." + excelModelVO.getExcelType();
        int pageNum = 1;
        setData(excelModelVO, null, destFilePath, vo, pageNum);
        String fileName = "学校绑定预警数据" + DateUtil.today();
        if (org.apache.commons.lang3.StringUtils.isBlank(fileName)) {
            fileName =
                    excelModelVO.getModelName()
                            + "_"
                            + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN)
                            + "."
                            + (org.apache.commons.lang3.StringUtils.isBlank(excelModelVO.getExcelType())
                            ? "xls"
                            : excelModelVO.getExcelType());
        }
        File file = FileUtil.file(destFilePath);
        BufferedInputStream in = null;
        try {
            in = FileUtil.getInputStream(file);
            ServletUtil.write(response, in, "application/octet-stream;charset=utf-8", fileName);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            FileUtil.del(destFilePath);
        }
    }

    @OpApi(
            funcCode = "eduAddressTree0018",
            title = "地址识别并匹配学区（查询）",
            funcType = FuncTypeEnum.query,
            checkPrivilege = BoolEnum.FALSE
    )
    @Override
    public AddressInfoAndSchool addressParseAndMatchSchool(AddressReqVO vo) {
        String address = vo.getAddress();
        if (ObjectUtils.isEmpty(address)) {
            throw new AppException("请输入需要查询的地址");
        }
        return parseAndMatchSchool(address);
    }

    @OpApi(
            funcCode = "eduAddressTree0029",
            title = "新地址识别并匹配学区(保存)",
            funcType = FuncTypeEnum.insert)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addressMatchSchoolAndSave(AddressReqVO vo) {
        if (StringUtils.isEmpty(vo.getAddressId())) {
            throw new AppException("要匹配的地址不能为空");
        }
        //目的：调用地址解析方法  将解析结果去识别学区  然后建立地址和学校关联
        EduAddress eduAddress = eduAddressService.getById(vo.getAddressId());
        if (eduAddress == null) {
            throw new AppException("地址不存在，请检查数据后重试!");
        }
        if (StringUtils.isEmpty(vo.getHandleStatus())) {
            //如果没有传入审核状态，就默认处理待处理的数据
            if (!HandleStatusEnum.NODO.getType().equals(eduAddress.getHandleStatus())) {
                throw new AppException("该地址不是待处理数据!");
            }
        }
        if (StringUtils.isEmpty(eduAddress.getAddressName())) {
            throw new AppException("地址数据为空，无法匹配!");
        }
        //1.解析地址
        AddressInfo addressInfo = smartParse.parseAddressInfo(eduAddress.getAddressName());
        //2.保存节点信息和学校关联 --同事物
        saveNodeMatchSchool(addressInfo, eduAddress, vo.getAddressId());
    }

    @Override
    @Transactional
    public void newAddressParse(AddressReqVO vo) {
        this.addressMatchSchoolAndSave(vo);
    }

    @Override
    @Transactional
    public void saveAddressSchoolAudit(AddressReqVO vo) {
        this.saveAddressSchoolAudit(vo.getAddressId());
    }

    @Override
    @OpApi(funcCode = "eduAddressTree0030", title = "地址重新解析到树节点中", funcType = FuncTypeEnum.other)
    public void addressAfreshParse(EduAddressTreeInitialQueryVO vo) {
        List<String> addressIds = vo.getAddressIds();
        if (CollectionUtils.isEmpty(addressIds)) {
            throw new AppException("未传入要解析的地址主键，无法进行解析！");
        }
        //查询所传入的id
        List<EduAddress> eduAddresses = eduAddressService.listByIds(addressIds);
        if (CollectionUtils.isEmpty(eduAddresses)) {
            throw new AppException("查询不到要解析的地址信息，请检查传入的地址主键是否有效！");
        }
        for (EduAddress eduAddress : eduAddresses) {
            this.performAddressAfreshParseOne(eduAddress);
        }
    }


    /**
     * 采用编程式事务
     * 执行单条地址解析
     *
     * @param eduAddress
     */
    private void performAddressAfreshParseOne(EduAddress eduAddress) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    addressAfreshParseOne(eduAddress);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    throw e;
                }
            }
        });
    }

    public void addressAfreshParseOne(EduAddress eduAddress) {
        //处理成功的地址,有addressTreeId表示就是处理成功了
        if (!StringUtils.isEmpty(eduAddress.getAddressTreeId())) {
            //校验同一个地址叶子节点，是否有多条数据
            Boolean duplicateFlag = isDuplicateLeafNode(eduAddress);
            if (!duplicateFlag) {
                //根据最后一个节点，获取树节点链路信息
                List<EduAddressTree> treeNodes = eduAddressTreeService.getLinkByTreeId(eduAddress.getAddressTreeId());
                if (!CollectionUtils.isEmpty(treeNodes)) {
                    //1。解绑edu_address_tree_school中的关联信息
                    unboundSchoolAndAddressTree(eduAddress, treeNodes);
                    //2.将edu_address_tree中的节点数据进行删除 （通过lastNode向上遍历）
                    eduAddressTreeService.removeByTreeLink(treeNodes);
                }
                //3.清除地址表中address_tree_id 数据
                eduAddress.setAddressTreeId(null);
                eduAddressService.updateById(eduAddress);
            } else {
                //修改地址状态，将树节点绑定置空，该地址不做解析
                eduAddress.setHandleMessage("绑定的叶子节点数据多条地址[" + eduAddress.getAddressTreeId() + "]请处理完后重试！");
                eduAddress.setAddressTreeId(null);
                eduAddress.setHandleStatus(EduAddressTreeHandleStatusTypeEnum.HANDLEERROR.getType());
                eduAddress.setHandleTime(new Date());
                eduAddressService.updateById(eduAddress);
                //修改地址状态后，不执行地址解析
                return;
            }
        }
        //3.根据地址和学校是否有关联来重新解析地址
        parseAddressBySchool(eduAddress);
    }

    /**
     * 保存解析的地址节点，并且匹配学区
     *
     * @param addressInfo
     * @param eduAddress
     * @param addressId
     */
    private void saveNodeMatchSchool(AddressInfo addressInfo, EduAddress eduAddress, String addressId) {
        try {
            saveNodeAndSchool(addressInfo, eduAddress, addressId);
        } catch (Exception e) {
            log.error("解析地址失败，解析地址为：{}，解析失败原因：{}", eduAddress.getAddressId(), e.getMessage());
            String msg = e.getMessage();
            if (org.apache.commons.lang3.StringUtils.isNotBlank(msg) && msg.length() > 300) {
                msg = msg.substring(0, 200);
            }
            UpdateWrapper<EduAddress> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda()
                    .eq(EduAddress::getAddressId, eduAddress.getAddressId())
                    .set(EduAddress::getHandleStatus, EduAddressTreeHandleStatusTypeEnum.HANDLEERROR.getType())
                    .set(EduAddress::getHandleMessage, msg)
                    .set(EduAddress::getHandleTime, eduAddress.getHandleTime())
                    .set(EduAddress::getUpdateTime, new Date());
            eduAddressService.update(updateWrapper);
        }
    }

    /**
     * 新地址解析后，生成地址树并执行匹配学区
     *
     * @param addressInfo
     * @param eduAddress
     * @param addressId
     */
    private void saveNodeAndSchool(AddressInfo addressInfo, EduAddress eduAddress, String addressId) {
        //1.将解析地址的结果，转换为树节点保存到数据库中
        initialIntegration.saveTreeNodeByAddressInfo(addressInfo, eduAddress, false);
        //2.查一遍地址是否解析成功生成地址节点
        eduAddress = eduAddressService.getById(addressId);
        if (StringUtils.isEmpty(eduAddress.getAddressTreeId()) || !HandleStatusEnum.success.getType().equals(eduAddress.getHandleStatus())) {
            //地址解析失败，未生成地址树节点，结束
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "地址状态未解析成功");
            return;
        }
        List<EduAddressTree> treeLinks = eduAddressTreeService.getLinkByTreeId(eduAddress.getAddressTreeId());
        if (CollectionUtils.isEmpty(treeLinks)) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "地址树节点为空");
            return;
        }
        //3.比对学区
        Map<String, List<EduSchoolVO>> schoolMap = matchSchoolByAddressInfo(treeLinks);
        if (CollectionUtils.isEmpty(schoolMap)) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "未匹配到学区");
            return;
        }
        if (CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.PRIMARY.getType())) &&
                CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.JUNIOR.getType()))) {
            //如果小学初中，都未匹配到，修改地址状态后返回
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "没有匹配到小学和初中");
            return;
        }
        //4.是否直接保存到edu_address_tree_school开关
        Boolean checkFlag = checkSchool(schoolMap);//自动审核的标志
        EduAddressTree eduAddressTree = checkAffirmFlag(treeLinks);//锁定的标志
        Boolean saveFlag = false;//保存到待审核表是否成功标志
        Boolean autoFlag = false;//锁定后自动审核是否成功标志
        if (checkFlag) {
            //TODO 待完成
            //1.保存到edu_address_school

            //2.并且将节点信息关联生成到edu_address_tree_school
        } else if (!ObjectUtils.isEmpty(eduAddressTree)) {
            // 取出小学，初中学校
            HashMap<String, List<EduSchoolVO>> matchSchools = new HashMap<>();
            List<EduSchoolVO> primarySchoolList = new ArrayList<>();
            List<EduSchoolVO> middleSchoolList = new ArrayList<>();
            // 获取 primarySchool 字段，并判空处理
            String primarySchool = eduAddressTree.getPrimarySchool();
            List<String> primaryList = new ArrayList<>();
            if (primarySchool != null && !primarySchool.isEmpty()) {
                primaryList = Arrays.stream(primarySchool.split(",")).collect(Collectors.toList());
            }
            // 获取 middleSchool 字段，并判空处理
            String middleSchool = eduAddressTree.getMiddleSchool();
            List<String> middleList = new ArrayList<>();
            if (middleSchool != null && !middleSchool.isEmpty()) {
                middleList = Arrays.stream(middleSchool.split(",")).collect(Collectors.toList());
            }
            ArrayList<String> schoolList = new ArrayList<>();
            schoolList.addAll(primaryList);
            schoolList.addAll(middleList);

            if (!CollectionUtils.isEmpty(primaryList)) {
                for (String schoolId : primaryList) {
                    EduSchoolVO schoolVO = eduSchoolService.getDetail(schoolId);
                    primarySchoolList.add(schoolVO);
                }
            }
            if (!CollectionUtils.isEmpty(middleList)) {
                for (String schoolId : middleList) {
                    EduSchoolVO schoolVO = eduSchoolService.getDetail(schoolId);
                    middleSchoolList.add(schoolVO);
                }
            }
            matchSchools.put(SchoolCategoryEnum.PRIMARY.getType(), primarySchoolList);
            matchSchools.put(SchoolCategoryEnum.JUNIOR.getType(), middleSchoolList);
            //如果锁定的初中或者小学为空，把锁定为空的地址推送给学校
            if (CollectionUtils.isEmpty(matchSchools.get(SchoolCategoryEnum.PRIMARY.getType()))) {
                matchSchools.put(SchoolCategoryEnum.PRIMARY.getType(), schoolMap.get(SchoolCategoryEnum.PRIMARY.getType()));
            }
            if (CollectionUtils.isEmpty(matchSchools.get(SchoolCategoryEnum.JUNIOR.getType()))) {
                matchSchools.put(SchoolCategoryEnum.JUNIOR.getType(), schoolMap.get(SchoolCategoryEnum.JUNIOR.getType()));
            }
            try {
                // 如果父节点状态是锁定，则需要将子节点也锁定掉
                if (eduAddressTree.getAffirmFlag().equals(1)) {
                    EduAddressTreeVO eduAddressTreeVO = Convert.convert(EduAddressTreeVO.class, eduAddressTree);
                    lockDescendants(eduAddressTreeVO, schoolList, treeLinks, eduAddressTree.getPrimarySchool(), eduAddressTree.getMiddleSchool());
                }

                // 如果没抛异保存到edu_address_school_audit
                ArrayList<EduAddressSchoolAudit> eduAddressSchoolAudits = saveToSchoolAuditList(matchSchools, eduAddress);
                if (CollectionUtils.isEmpty(eduAddressSchoolAudits)) {
                    throw new AppException("保存到待审核表失败");
                } else {
                    // 自动审核通过,保存到edu_address_school
                    for (EduAddressSchoolAudit eduAddressSchoolAudit : eduAddressSchoolAudits) {
                        //如果学校ID在锁定名单里面，自动锁定，如果没有，则不锁定
                        if (schoolList.contains(eduAddressSchoolAudit.getSchoolId())) {
                            EduAddressSchoolAuditVO eduAddressSchoolAuditVO = new EduAddressSchoolAuditVO();
                            eduAddressSchoolAuditVO.setId(eduAddressSchoolAudit.getId());
                            eduAddressSchoolAuditVO.setAddressId(eduAddressSchoolAudit.getAddressId());
                            eduAddressSchoolAuditVO.setAuthStatus("1");
                            // 自动审核通过只能是正学区
                            eduAddressSchoolAuditVO.setNativeFlag("0");
                            eduAddressSchoolAuditVO.setServiceAreaFlag("0");
                            eduAddressSchoolAuditVO.setAuthRemarks("自动审核");
                            eduAddressSchoolAuditVO.setSchoolId(eduAddressSchoolAudit.getSchoolId());
                            eduAddressSchoolIntegration.addressAuditAuto(eduAddressSchoolAuditVO);
                        }
                    }
                }
                autoFlag = true;
            } catch (Exception e) {
                // 抛了异常保存到审核表去，让学校审核；
                saveFlag = saveToSchoolAudit(schoolMap, eduAddress);
                autoFlag = false;
            }
        } else {
            //1.保存到edu_address_school_audit
            saveFlag = saveToSchoolAudit(schoolMap, eduAddress);
        }
        //5. 修改地址状态  匹配状态 ->待审核  等审核成功后，将地址状态改为审核成功
        if (saveFlag && !autoFlag) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.PEND, null);
        }else if(autoFlag){
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.SUCCESS, "自动审核成功");
        }else {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "生成待审核数据有误");
        }
    }

    public void saveAddressSchoolAudit(String addressId) {
        //2.查一遍地址是否解析成功生成地址节点
        EduAddress eduAddress = eduAddressService.getById(addressId);
        if (StringUtils.isEmpty(eduAddress.getAddressTreeId()) || !HandleStatusEnum.success.getType().equals(eduAddress.getHandleStatus())) {
            return;
        }
        ArrayList<String> matchList = new ArrayList<>();
        matchList.add(MatchingStatusEnum.NODO.getType());
        matchList.add(MatchingStatusEnum.FAIL.getType());
        if (eduAddress.getMatchingStatus() != null && !matchList.contains(eduAddress.getMatchingStatus())) {
            return;
        }
        List<EduAddressTree> treeLinks = eduAddressTreeService.getLinkByTreeId(eduAddress.getAddressTreeId());
        if (CollectionUtils.isEmpty(treeLinks)) {
            return;
        }
        //3.比对学区
        Map<String, List<EduSchoolVO>> schoolMap = matchSchoolByAddressInfo(treeLinks);
        if (CollectionUtils.isEmpty(schoolMap)) {
            return;
        }
        if (CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.PRIMARY.getType())) &&
                CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.JUNIOR.getType()))) {
            //如果小学初中，都未匹配到，修改地址状态后返回
            return;
        }
        //4.是否直接保存到edu_address_tree_school开关
        Boolean checkFlag = checkSchool(schoolMap);
        Boolean saveFlag = false;
        if (checkFlag) {
            //TODO 待完成
            //1.保存到edu_address_school

            //2.并且将节点信息关联生成到edu_address_tree_school
        } else {
            //1.保存到edu_address_school_audit
            saveFlag = saveToSchoolAudit(schoolMap, eduAddress);
        }
        //5. 修改地址状态  匹配状态 ->待审核  等审核成功后，将地址状态改为审核成功
        if (saveFlag) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.PEND, null);
        } else {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "生成待审核数据有误");
        }
    }


    /**
     * 匹配学区并且保存，不解析地址
     *
     * @param eduAddress
     */
    @Transactional(rollbackFor = Exception.class)
    public void matchSchoolAndSave(EduAddress eduAddress) {
        List<EduAddressTree> treeLinks = eduAddressTreeService.getLinkByTreeId(eduAddress.getAddressTreeId());
        if (CollectionUtils.isEmpty(treeLinks)) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "地址树节点为空");
            return;
        }
        //3.比对学区
        Map<String, List<EduSchoolVO>> schoolMap = matchSchoolByAddressInfo(treeLinks);
        if (CollectionUtils.isEmpty(schoolMap)) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "未匹配到学区");
            return;
        }
        if (CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.PRIMARY.getType())) &&
                CollectionUtils.isEmpty(schoolMap.get(SchoolCategoryEnum.JUNIOR.getType()))) {
            //如果小学初中，都未匹配到，修改地址状态后返回
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "没有匹配到小学和初中");
            return;
        }
        Boolean checkFlag = checkSchool(schoolMap);
        Boolean saveFlag = false;
        if (checkFlag) {
            //TODO 待完成
            //1.保存到edu_address_school

            //2.并且将节点信息关联生成到edu_address_tree_school
        } else {
            //1.保存到edu_address_school_audit
            saveFlag = saveToSchoolAudit(schoolMap, eduAddress);
        }
        //5. 修改地址状态  匹配状态 ->待审核  等审核成功后，将地址状态改为审核成功
        if (saveFlag) {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.PEND, null);
        } else {
            updateAddressMatchingStatus(eduAddress.getAddressId(), MatchingStatusEnum.FAIL, "生成待审核数据有误");
        }
    }

    private void updateAddressMatchingStatus(String addressId, MatchingStatusEnum matchingStatusEnum, String msg) {
        UpdateWrapper<EduAddress> updateWrapper = new UpdateWrapper<>();
        updateWrapper.lambda().eq(EduAddress::getAddressId, addressId)
                .set(EduAddress::getMatchingStatus, matchingStatusEnum.getType())
                .set(EduAddress::getMatchingMessage, matchingStatusEnum.getName())
                .set(EduAddress::getMatchingTime, new Date());
        if (org.apache.commons.lang3.StringUtils.isNotBlank(msg)) {
            updateWrapper.lambda().set(EduAddress::getMatchingMessage, matchingStatusEnum.getName() + ":" + msg);
        } else {
            updateWrapper.lambda().set(EduAddress::getMatchingMessage, matchingStatusEnum.getName());
        }
        eduAddressService.update(updateWrapper);
    }

    /**
     * 保存到edu_address_school_audit表中
     *
     * @param schoolMap
     * @param eduAddress return true:成功， false:失败
     */
    private Boolean saveToSchoolAudit(Map<String, List<EduSchoolVO>> schoolMap, EduAddress eduAddress) {
        //取出小学，初中学校
        List<EduSchoolVO> primarySchool = schoolMap.get(SchoolCategoryEnum.PRIMARY.getType());
        List<EduSchoolVO> juniorSchool = schoolMap.get(SchoolCategoryEnum.JUNIOR.getType());
        if (CollectionUtils.isEmpty(primarySchool) && CollectionUtils.isEmpty(juniorSchool)) {
            return false;
        }
        List<EduSchoolVO> schoolVOList = new ArrayList<>();
        schoolVOList.addAll(primarySchool);
        schoolVOList.addAll(juniorSchool);
        List<String> schoolIds = schoolVOList.stream().map(EduSchoolVO::getOrgId).collect(Collectors.toList());
        //地址表有关联数据的学校id
        Set<String> auditSuccessSchoolIds = new HashSet<>();
        //审核表中有 学校-地址 待审核数据未审核
        Set<String> beReviewedSchoolIds = new HashSet<>();
        //根据地址id和学校id判断地址学校关联表是否有数据
        QueryWrapper<EduAddressSchool> addressSchoolQueryWrapper = new QueryWrapper<>();
        addressSchoolQueryWrapper.lambda().eq(EduAddressSchool::getAddressId, eduAddress.getAddressId())
                .in(EduAddressSchool::getSchoolId, schoolIds);
        List<EduAddressSchool> eduAddressSchoolRelationList = eduAddressSchoolService.list(addressSchoolQueryWrapper);
        if (!CollectionUtils.isEmpty(eduAddressSchoolRelationList)) {
            for (EduAddressSchool eduAddressSchool : eduAddressSchoolRelationList) {
                auditSuccessSchoolIds.add(eduAddressSchool.getSchoolId());
            }
        }
        //根据以上学校和地址id查询审核表是否数据
        QueryWrapper<EduAddressSchoolAudit> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(EduAddressSchoolAudit::getAddressId, eduAddress.getAddressId())
                .in(EduAddressSchoolAudit::getSchoolId, schoolIds);
        List<EduAddressSchoolAudit> auditList = schoolAuditService.list(wrapper);
        if (!CollectionUtils.isEmpty(auditList)) {
            for (EduAddressSchoolAudit schoolAudit : auditList) {
                //如果审核通过
                if (AuthStatusEnum.YES.getType().equals(schoolAudit.getAuthStatus())) {
                    auditSuccessSchoolIds.add(schoolAudit.getSchoolId());
                } else if (AuthStatusEnum.NODO.getType().equals(schoolAudit.getAuthStatus())) {
                    //如果待审核状态
                    beReviewedSchoolIds.add(schoolAudit.getSchoolId());
                }
            }
        }

        //审核列表
        ArrayList<EduAddressSchoolAudit> addressSchoolAudits = new ArrayList<>();
        for (EduSchoolVO schoolVO : schoolVOList) {
            if (schoolVO != null) {
                EduAddressSchoolAudit schoolAudit = new EduAddressSchoolAudit();
                if (auditSuccessSchoolIds.contains(schoolVO.getOrgId())) {
                    //不添加待审核数据
                    continue;
                } else if (beReviewedSchoolIds.contains(schoolVO.getOrgId())) {
                    //不添加待审核数据 ,表中已有待审核数据
                    continue;
                }
                // 设置学校地址关联审核信息
                schoolAudit.setAddressId(eduAddress.getAddressId());
                schoolAudit.setAddressName(eduAddress.getAddressName());
                schoolAudit.setSchoolId(schoolVO.getOrgId());
                schoolAudit.setSchoolName(schoolVO.getSchoolName());
                schoolAudit.setSchoolCategory(schoolVO.getSchoolCategory());
                schoolAudit.setAreaCode(schoolVO.getAreaCode());
                schoolAudit.setTreeId(eduAddress.getAddressTreeId());
                schoolAudit.setNativeFlag("0");
                schoolAudit.setServiceAreaFlag("0");
                schoolAudit.setAuthStatus(AuthStatusEnum.NODO.getType());
                schoolAudit.setAuthTime(null);
                addressSchoolAudits.add(schoolAudit);
            }
        }
        //如果学校关联表有数据，把关联到edu_address_tree_school表中
        if (!CollectionUtils.isEmpty(auditSuccessSchoolIds)) {
            EduAddressTree addressTree = eduAddressTreeService.getById(eduAddress.getAddressTreeId());
            EduAddressTreeInitial lastNode = Convert.convert(EduAddressTreeInitial.class, addressTree);
            if (lastNode != null) {
                //将地址树节点和学校进行关联
                EduAddressTreeInitialVO lastInitialVO = Convert.convert(EduAddressTreeInitialVO.class, lastNode);
                eduAddressSchoolService.saveTreeSchoolRelation(eduAddress, lastInitialVO);
            }
        }
        if (CollectionUtils.isEmpty(addressSchoolAudits)) {
            return false;
        }
        //批量保存待审核数据
        schoolAuditService.saveBatch(addressSchoolAudits);
        return true;
    }

    /**
     * 保存到edu_address_school_audit表中
     *
     * @param schoolMap
     * @param eduAddress return true:成功， false:失败
     */
    private ArrayList<EduAddressSchoolAudit> saveToSchoolAuditList(Map<String, List<EduSchoolVO>> schoolMap, EduAddress eduAddress) {
        //取出小学，初中学校
        List<EduSchoolVO> primarySchool = schoolMap.get(SchoolCategoryEnum.PRIMARY.getType());
        List<EduSchoolVO> juniorSchool = schoolMap.get(SchoolCategoryEnum.JUNIOR.getType());
        if (CollectionUtils.isEmpty(primarySchool) && CollectionUtils.isEmpty(juniorSchool)) {
            return new ArrayList<>();
        }
        List<EduSchoolVO> schoolVOList = new ArrayList<>();
        schoolVOList.addAll(primarySchool);
        schoolVOList.addAll(juniorSchool);
        List<String> schoolIds = schoolVOList.stream().map(EduSchoolVO::getOrgId).collect(Collectors.toList());
        //地址表有关联数据的学校id
        Set<String> auditSuccessSchoolIds = new HashSet<>();
        //审核表中有 学校-地址 待审核数据未审核
        Set<String> beReviewedSchoolIds = new HashSet<>();
        //根据地址id和学校id判断地址学校关联表是否有数据
        QueryWrapper<EduAddressSchool> addressSchoolQueryWrapper = new QueryWrapper<>();
        addressSchoolQueryWrapper.lambda().eq(EduAddressSchool::getAddressId, eduAddress.getAddressId())
                .in(EduAddressSchool::getSchoolId, schoolIds);
        List<EduAddressSchool> eduAddressSchoolRelationList = eduAddressSchoolService.list(addressSchoolQueryWrapper);
        if (!CollectionUtils.isEmpty(eduAddressSchoolRelationList)) {
            for (EduAddressSchool eduAddressSchool : eduAddressSchoolRelationList) {
                auditSuccessSchoolIds.add(eduAddressSchool.getSchoolId());
            }
        }
        //根据以上学校和地址id查询审核表是否数据
        QueryWrapper<EduAddressSchoolAudit> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(EduAddressSchoolAudit::getAddressId, eduAddress.getAddressId())
                .in(EduAddressSchoolAudit::getSchoolId, schoolIds);
        List<EduAddressSchoolAudit> auditList = schoolAuditService.list(wrapper);
        if (!CollectionUtils.isEmpty(auditList)) {
            for (EduAddressSchoolAudit schoolAudit : auditList) {
                //如果审核通过
                if (AuthStatusEnum.YES.getType().equals(schoolAudit.getAuthStatus())) {
                    auditSuccessSchoolIds.add(schoolAudit.getSchoolId());
                } else if (AuthStatusEnum.NODO.getType().equals(schoolAudit.getAuthStatus())) {
                    //如果待审核状态
                    beReviewedSchoolIds.add(schoolAudit.getSchoolId());
                }
            }
        }

        //审核列表
        ArrayList<EduAddressSchoolAudit> addressSchoolAudits = new ArrayList<>();
        for (EduSchoolVO schoolVO : schoolVOList) {
            if (schoolVO != null) {
                EduAddressSchoolAudit schoolAudit = new EduAddressSchoolAudit();
                if (auditSuccessSchoolIds.contains(schoolVO.getOrgId())) {
                    //不添加待审核数据
                    continue;
                } else if (beReviewedSchoolIds.contains(schoolVO.getOrgId())) {
                    //不添加待审核数据 ,表中已有待审核数据
                    continue;
                }
                // 设置学校地址关联审核信息
                schoolAudit.setAddressId(eduAddress.getAddressId());
                schoolAudit.setAddressName(eduAddress.getAddressName());
                schoolAudit.setSchoolId(schoolVO.getOrgId());
                schoolAudit.setSchoolName(schoolVO.getSchoolName());
                schoolAudit.setSchoolCategory(schoolVO.getSchoolCategory());
                schoolAudit.setAreaCode(schoolVO.getAreaCode());
                schoolAudit.setTreeId(eduAddress.getAddressTreeId());
                schoolAudit.setNativeFlag("0");
                schoolAudit.setServiceAreaFlag("0");
                schoolAudit.setAuthStatus(AuthStatusEnum.NODO.getType());
                schoolAudit.setAuthTime(null);
                addressSchoolAudits.add(schoolAudit);
            }
        }
        //如果学校关联表有数据，把关联到edu_address_tree_school表中
        if (!CollectionUtils.isEmpty(auditSuccessSchoolIds)) {
            EduAddressTree addressTree = eduAddressTreeService.getById(eduAddress.getAddressTreeId());
            EduAddressTreeInitial lastNode = Convert.convert(EduAddressTreeInitial.class, addressTree);
            if (lastNode != null) {
                //将地址树节点和学校进行关联
                EduAddressTreeInitialVO lastInitialVO = Convert.convert(EduAddressTreeInitialVO.class, lastNode);
                eduAddressSchoolService.saveTreeSchoolRelation(eduAddress, lastInitialVO);
            }
        }
        if (CollectionUtils.isEmpty(addressSchoolAudits)) {
            return new ArrayList<>();
        }
        //批量保存待审核数据
        schoolAuditService.saveBatch(addressSchoolAudits);
        return addressSchoolAudits;
    }

    /**
     * 校验学区是否符合要求
     * false:保存到待审核
     * true:保存到edu_address_tree_school
     *
     * @param matchSchools
     * @return
     */
    private Boolean checkSchool(Map<String, List<EduSchoolVO>> matchSchools) {
        CommonVO commonVO = new CommonVO();
        commonVO.setParamKey("MATCH_SCHOOL_CHECK_SAVE_FLAG");
        String schoolCheckFlag = commonIntegration.getValueByKey(commonVO);
        if (StringUtils.hasText(schoolCheckFlag)) {
            schoolCheckFlag = BoolEnum.FALSE.getType();
        }
        if (!BoolEnum.TRUE.getType().equals(schoolCheckFlag)) {
            return false;
        }
        //判断学区是否符合要求
        Boolean flag = true;
        List<EduSchoolVO> primarySchool = matchSchools.get(SchoolCategoryEnum.PRIMARY.getType());
        return false;
    }

    /**
     * 是否锁定的标志
     * false:没有锁定
     * true:已经锁定，直接审核通过
     *
     * @param treeLinks
     * @return
     */
    private EduAddressTree checkAffirmFlag(List<EduAddressTree> treeLinks) {
        if (treeLinks == null) {
            return null;
        }

        for (EduAddressTree treeLink : treeLinks) {
            if (treeLink != null && treeLink.getAffirmFlag() != null && treeLink.getAffirmFlag().equals(1)) {
                return treeLink;
            }
        }
        return null;
    }


    /**
     * 解析并识别学区
     *
     * @param address
     * @return
     */
    private AddressInfoAndSchool parseAndMatchSchool(String address) {
        // 解析地址
        AddressInfo addressInfo = smartParse.parseAddressInfo(address);
        if (addressInfo.getErrorStatus() == 1) {
            throw new AppException(addressInfo.getErrorDetail());
        }
        List<EduAddressTreeInitialVO> parseNodes = addressInfo.getParseNodes();
        if (CollectionUtils.isEmpty(parseNodes)) {
            return new AddressInfoAndSchool();
        }
        //根据地址解析结果返回学区信息
        Map<String, List<EduSchoolVO>> schoolCategoryMap = matchSchoolByAddressInfo(addressInfo);
        AddressInfoAndSchool result = Convert.convert(AddressInfoAndSchool.class, addressInfo);
        result.setMatchSchools(schoolCategoryMap);
        return result;
    }

    /**
     * 根据地址匹配的结果，返回匹配的学区信息
     *
     * @param addressInfo
     * @return
     */
    @Override
    public Map<String, List<EduSchoolVO>> matchSchoolByAddressInfo(AddressInfo addressInfo) {
        List<EduAddressTreeInitialVO> parseNodes = addressInfo.getParseNodes();
        SchoolCategoryEnum[] values = SchoolCategoryEnum.values();
        Map<String, List<EduSchoolVO>> matchSchools = new HashMap<>((int) (values.length / 0.75D + 1));
        for (SchoolCategoryEnum value : values) {
            List<EduSchoolVO> eduSchoolVOS = eduAddressTreeSchoolService.addressMatchSchool(value.getType(), parseNodes);
            matchSchools.put(value.getType(), eduSchoolVOS);
        }
        return matchSchools;
    }


    public Map<String, List<EduSchoolVO>> matchSchoolByAddressInfo(List<EduAddressTree> treeLinks) {
        if (CollectionUtils.isEmpty(treeLinks)) {
            return new HashMap<>();
        }
        SchoolCategoryEnum[] values = SchoolCategoryEnum.values();
        Map<String, List<EduSchoolVO>> matchSchools = new HashMap<>((int) (values.length / 0.75D + 1));
        for (SchoolCategoryEnum value : values) {
            List<EduSchoolVO> eduSchoolVOS = eduAddressTreeSchoolService.addressMatchSchoolByTreeLink(value.getType(), treeLinks);
            matchSchools.put(value.getType(), eduSchoolVOS);
        }
        return matchSchools;
    }

    /**
     * 根据地址是否有学校关联，来进行解析
     *
     * @param eduAddress
     */
    private void parseAddressBySchool(EduAddress eduAddress) {
        //判断地址是否关联了学区信息
        QueryWrapper<EduAddressSchool> queryWrapper = new QueryWrapper<>();

        Integer count = 0;
        List<String> categoryList = new ArrayList<>();
        String bnxxFlag = eduAddress.getBnxxFlag();
        String bnczFlag = eduAddress.getBnczFlag();
        if (BoolEnum.TRUE.getType().equals(bnxxFlag)) {
            categoryList.add(SchoolCategoryEnum.PRIMARY.getType());
            count = count + 1;
        }
        if (BoolEnum.TRUE.getType().equals(bnczFlag)) {
            categoryList.add(SchoolCategoryEnum.JUNIOR.getType());
            count = count + 1;
        }
        queryWrapper.lambda().eq(EduAddressSchool::getAddressId, eduAddress.getAddressId())
                .eq(EduAddressSchool::getServiceAreaFlag, BoolEnum.FALSE.getType())
                .in(!CollectionUtils.isEmpty(categoryList), EduAddressSchool::getSchoolCategory, categoryList);
        long relationCount = eduAddressSchoolService.count(queryWrapper);
        if (relationCount >= count) {
            //1.初始化学区,解析单挑地址到树节点
            initialIntegration.parseAddressTree(eduAddress, true);
        } else {
            //地址没有任何关联本学段数据
            //检查审核表中是否有待审数据，有数据就删除
            QueryWrapper<EduAddressSchoolAudit> wrapper = new QueryWrapper<>();
            wrapper.lambda().eq(EduAddressSchoolAudit::getAddressId, eduAddress.getAddressId());
            List<EduAddressSchoolAudit> auditList = schoolAuditService.list(wrapper);
            if (!CollectionUtils.isEmpty(auditList)) {
                //有数据就删除
                schoolAuditService.removeByIds(auditList.stream().map(EduAddressSchoolAudit::getId).collect(Collectors.toList()));
            }
            //然后再去 重新解析
            AddressReqVO addressReqVO = new AddressReqVO();
            addressReqVO.setHandleStatus(eduAddress.getHandleStatus());
            addressReqVO.setAddressId(eduAddress.getAddressId());
            //1.不初始化学区,当成新地址去重新解析
            addressMatchSchoolAndSave(addressReqVO);
        }
    }

    /**
     * 解绑edu_address_tree_school中的关联信息
     * 1.解绑  removeSchoolAndAddressTreeRelation
     * 2.置空treeId
     *
     * @param eduAddress
     * @param treeNodes
     */
    private void unboundSchoolAndAddressTree(EduAddress eduAddress, List<EduAddressTree> treeNodes) {
        if (CollectionUtils.isEmpty(treeNodes)) {
            return;
        }
        //根据地址主键，从edu_address_school中查询出关联数据
        QueryWrapper<EduAddressSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(EduAddressSchool::getAddressId, eduAddress.getAddressId());
        List<EduAddressSchool> eduAddressSchools = eduAddressSchoolService.list(queryWrapper);
        if (CollectionUtils.isEmpty(eduAddressSchools)) {
            //TODO 没有数据需要去处理树节点与地址表关联的数据
            return;
        }
        for (EduAddressSchool eduAddressSchool : eduAddressSchools) {
            //删除树节点和学校之间的关系
            if (!StringUtils.isEmpty(eduAddress.getAddressTreeId())) {
                eduAddressTreeSchoolService.
                        removeSchoolAndAddressTreeRelation(eduAddressSchool.getSchoolId(), treeNodes);
            }
            //将edu_address_school中TreeId置空
            if (!StringUtils.isEmpty(eduAddressSchool.getTreeId())) {
                eduAddressSchool.setTreeId(null);
                eduAddressSchoolService.updateById(eduAddressSchool);
            }
        }

    }

    /**
     * 校验同一个地址树节点是否关联了多个地址
     *
     * @param eduAddress
     * @return false:叶子节点未关联多个地址
     * true：叶子节点关联了多个地址
     */
    private Boolean isDuplicateLeafNode(EduAddress eduAddress) {
        QueryWrapper<EduAddress> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(EduAddress::getAddressTreeId, eduAddress.getAddressTreeId());
        long relationCount = eduAddressService.count(queryWrapper);
        if (relationCount > 1) {
            return true;
        }
        //表示没有关联多个地址
        return false;
    }

    private void setData(ExcelModelVO excelModelVO, ExcelWriter excelWriter, String destFilePath, AddressBoundCheckVO vo, int pageNum) {
        vo.setPageNum(pageNum);
        vo.setPageSize(500);
        boolean closeFlag = false;
        try {
            PageInfo<AddressBoundCheckVO> pageInfo = queryBoundLessAsLeafNodes(vo);
            String total = String.valueOf(pageInfo.getTotal());
            List<Map<String, Object>> list = new ArrayList<>();
            if (!CollectionUtils.isEmpty(pageInfo.getList())) {
                for (AddressBoundCheckVO addressBoundCheckVO : pageInfo.getList()) {
                    list.add(BeanUtil.beanToMap(addressBoundCheckVO, false, true));
                }
            }
            if (pageNum == 1) {
                if (excelWriter == null) {
                    excelWriter = excelCommonHelper.createExcelWriter(excelModelVO, destFilePath, Integer.parseInt(total));
                }
                excelCommonHelper.writeMap(excelWriter, list);
            } else {
                excelCommonHelper.writeMap(excelWriter, list);
            }
            if (pageInfo.isHasNextPage()) {
                pageNum = pageNum + 1;
                setData(excelModelVO, excelWriter, destFilePath, vo, pageNum);
            } else {
                closeFlag = true;
            }
        } catch (AppException appEx) {
            throw appEx;
        } catch (Exception e) {
            throw new AppException("调用自定义写入服务异常:" + e.getMessage());
        } finally {
            if (closeFlag && excelWriter != null) {
                excelWriter.close();
            }
        }

    }

    /**
     * 返回和school有绑定关系的叶子节点数据
     *
     * @param list
     * @return
     */
    private List<AddressBoundCheckVO> takeOutBoundLeftNode(List<AddressBoundCheckVO> list) {
        ArrayList<AddressBoundCheckVO> targetList = new ArrayList<>();
        //先取出所有叶子节点数据
        for (AddressBoundCheckVO item : list) {
            if (item.getLeafNode() == 1) {
                targetList.add(item);
            }
            //需要继续往下找，不需要考虑绑定节点状况
            List<AddressBoundCheckVO> asLeafNodes = eduAddressTreeService.queryBoundLeafNode(item.getTreeId(), item.getSchoolId());
            List<AddressBoundCheckVO> addressBoundCheckVOS = takeOutBoundLeftNode(asLeafNodes);
            targetList.addAll(addressBoundCheckVOS);
        }
        return targetList;
    }

    public PageInfo<AddressTreeForSchoolVO> getSchoolListByParentId(AddressTreeReqVO addressTreeReqVO) {
        PageInfo<AddressTreeVO> result = this.getListByParentId(addressTreeReqVO);
        List<AddressTreeForSchoolVO> list = Convert.toList(AddressTreeForSchoolVO.class, result.getList());
        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        list = list.stream().peek(item -> {
            queryWrapper.lambda()
                    .eq(EduAddressTreeSchool::getSchoolId, SessionUtil.getOrgId())
                    .eq(EduAddressTreeSchool::getTreeId, item.getAddressId());
            EduAddressTreeSchool treeSchool = eduAddressTreeSchoolService.getOne(queryWrapper);
            if (treeSchool != null) {
                item.setBoundLeafNum(treeSchool.getBoundLeafNum());
            } else {
                item.setBoundLeafNum(0);
            }
            queryWrapper.clear();
        }).collect(Collectors.toList());
        return PagerUtil.parsePagerVo(list, result);
    }

    /**
     * 将地址树节点转化为esVO
     *
     * @param entity 地址树节点实体
     * @return AddressEsVO
     * @author fangs
     */
    private AddressEsVO entityToESVO(EduAddressTree entity) {
        Map<String, Object> data = selectOne(entity);
        AddressEsVO addressEsVO = Convert.convert(AddressEsVO.class, data);
        addressEsVO.setAreaType(entity.getAreaType());
        addressEsVO.setAddressId(entity.getAddressId());
        return addressEsVO;
    }


    /**
     * 检查更新数据中是否有用于地址解析的节点，有则更新es
     *
     * @param eduAddressTreeVOList 更新的数据集合
     * @author fangs
     */
    private void syncES(List<EduAddressTreeVO> eduAddressTreeVOList) {
        if (CollectionUtils.isEmpty(eduAddressTreeVOList)) {
            return;
        }
        for (EduAddressTreeVO treeId : eduAddressTreeVOList) {
            syncES(treeId);
        }
    }

    /**
     * 检查更新数据中是否有用于地址解析的节点，有则更新es
     *
     * @param eduAddressTreeVO 更新数据
     * @author fangs
     */
    private void syncES(EduAddressTreeVO eduAddressTreeVO) {
        if (ObjectUtils.isEmpty(eduAddressTreeVO)) {
            return;
        }
        if (ES_ENABLE_NODE.contains(eduAddressTreeVO.getAreaType())) {
            AddressTree addressTree = AddressTree.getAddressTree(eduAddressTreeVO);
            AddressEsVO addressVO = (AddressEsVO) addressTree.getAddressVO();
            addressVO.setAreaType(eduAddressTreeVO.getAreaType());
            addressMessageService.updateAddressData(addressVO);
        }
    }


    /**
     * 查询父级节点
     *
     * @param parentId 父级id
     * @return
     */
    private EduAddressTree selectParentNode(Long parentId) {
        QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(EduAddressTree::getParentId, EduAddressTree::getAreaType, EduAddressTree::getAreaCode,
                        EduAddressTree::getAreaName, EduAddressTree::getSearchTerm, EduAddressTree::getAddressId)
                .eq(EduAddressTree::getAddressId, parentId);
        return eduAddressTreeService.getOne(queryWrapper);
    }

    /**
     * 根据传入的节点，补全父级节点信息
     *
     * @param node
     * @return
     */
    private Map<String, Object> selectOne(EduAddressTree node) {
        long parentId = node.getParentId();
        Map<String, Object> data = new HashMap<>((int) (34 / 0.75F + 1));
        data.put("addressId", node.getAddressId());
        data.put(node.getAreaType(), node.getAreaName());
        data.put("fullAddress", node.getFullAddress());
        data.put(node.getAreaType() + "_id", node.getAddressId());
        if (StringUtils.hasText(node.getAreaCode())) {
            data.put(node.getAreaType() + "Code", node.getAreaCode());
        }
        if (StringUtils.hasText(node.getSearchTerm())) {
            data.put(node.getAreaType() + "Alias", node.getSearchTerm());
        } else {
            data.put(node.getAreaType() + "Alias", node.getAreaName());
        }
        //获取所有父级节点
        this.getFullArea(parentId, data);

        if (!StringUtils.hasText((CharSequence) data.get("provinceAlias"))) {
            String province = (String) data.get("province");
            data.put("provinceAlias", this.getAreaAlias(PROVINCE_SUFFIX, province));
        }

        if (!StringUtils.hasText((CharSequence) data.get("cityAlias"))) {
            String city = (String) data.get("city");
            data.put("cityAlias", this.getAreaAlias(CITY_SUFFIX, city));
        }

        if (!StringUtils.hasText((CharSequence) data.get("districtAlias"))) {
            String district = (String) data.get("district");
            data.put("districtAlias", this.getAreaAlias(DISTRICT_SUFFIX, district));
        }
        return data;
    }

    /**
     * 获取所有父级节点
     *
     * @param parentId 父级id
     * @param data
     * @author fangs
     */
    private void getFullArea(Long parentId, Map<String, Object> data) {
        do {
            EduAddressTree parent = selectParentNode(parentId);
            if (parent == null) {
                break;
            }
            data.put(parent.getAreaType(), parent.getAreaName());
            data.put(parent.getAreaType() + "_id", parent.getAddressId());
            if (StringUtils.hasText(parent.getAreaCode())) {
                data.put(parent.getAreaType() + "Code", parent.getAreaCode());
            }
            if (StringUtils.hasText(parent.getSearchTerm())) {
                data.put(parent.getAreaType() + "Alias", parent.getSearchTerm());
            } else {
                data.put(parent.getAreaType() + "Alias", parent.getAreaName());
            }
            parentId = parent.getParentId();
        } while (parentId != 0);
    }

    /**
     * 解析地区名称获取别名
     *
     * @param aliasSuffix 可能得后缀名
     * @param areaName    地区名称
     * @return 别名，使用'#'分隔
     * @author fangs
     */
    private String getAreaAlias(List<String> aliasSuffix, String areaName) {
        StringBuilder aliasBuf = new StringBuilder(areaName);
        for (String provinceSuffix : aliasSuffix) {
            if (areaName.endsWith(provinceSuffix)) {
                aliasBuf.append(AddressTree.SEARCH_TERM_SEPARATOR).append(areaName, 0, areaName.lastIndexOf(provinceSuffix));
            }
        }
        return aliasBuf.toString();
    }

    /**
     * 更新数据库地址信息-ES和mysql
     *
     * @param addressVO 格式化的地址
     * @author fangs
     */
    private Long saveAddressInfo(AddressVO addressVO) {
        List<AddressTreeVO> treeVOList = addressVO.getAddressChain();
        if (CollectionUtils.isEmpty(treeVOList)) {
            throw new AppException("地址链缺失");
        }
        //修改地址树前先对涉及到地址树节点加读锁，防止地址管理功能和
        List<RLock> rLockList = new ArrayList<>(12);
        try {
            for (AddressTreeVO addressTreeVO : treeVOList) {
                if (addressTreeVO.getAddressId() == null) {
                    continue;
                }
                RLock rLock = redissonClient.getReadWriteLock(EduCache.EDU_ADDRESS_TREE_NODE_LOCK + addressTreeVO.getAddressId()).readLock();
                boolean res = rLock.tryLock();
                if (res) {
                    rLockList.add(rLock);
                } else {
                    throw new AppException("地址树正在维护中，请稍后重试。");
                }
            }
        } catch (Exception e) {
            this.unLock(rLockList);
            throw e;
        }
        //获取锁成功，同步地址树节点
        try {
            return this.insertTree(addressVO);
        } finally {
            this.unLock(rLockList);
        }
    }

    private void unLock(List<RLock> rLockList) {
        for (RLock rLock : rLockList) {
            rLock.unlock();
        }
    }

    /**
     * 地址树插入
     *
     * @param addressVO 地址格式化信息
     * @return 叶子节点主键id
     * @author fangs
     */
    private Long insertTree(AddressVO addressVO) {
        List<AddressTreeVO> treeVOList = addressVO.getAddressChain();
        long parentId = 0;
        for (AddressTreeVO addressTreeVO : treeVOList) {
            Long addressId = addressTreeVO.getAddressId();
            if (addressId != null) {
                if (addressTreeVO.getLeafNode() == 1) {
                    UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().eq(EduAddressTree::getAddressId, addressId)
                            .set(EduAddressTree::getLeafNode, 1);
                    eduAddressTreeService.update(updateWrapper);
                    EduAddressTreeVO eduAddressTreeVO = Convert.convert(EduAddressTreeVO.class, addressTreeVO);
                    eduAddressTreeVO.setChangeType(DataSaveEnum.update);
                    TreeChangeHelper.addTreeChange(eduAddressTreeVO);
                }
                parentId = addressId;
            } else {
                //VO中若没有es数据则先查询再更新
                QueryWrapper<EduAddressTree> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().select(EduAddressTree::getParentId, EduAddressTree::getAreaName, EduAddressTree::getAddressId)
                        .eq(EduAddressTree::getParentId, parentId)
                        .eq(EduAddressTree::getAreaName, addressTreeVO.getAreaName());
                EduAddressTree eduAddressTree = eduAddressTreeService.getOne(queryWrapper);
                addressTreeVO.setParentId(parentId);
                if (eduAddressTree == null) {
                    eduAddressTree = Convert.convert(EduAddressTree.class, addressTreeVO);
                    eduAddressTree.setAddressId(null);
                    eduAddressTreeService.saveNewNode(eduAddressTree);

                    EduAddressTreeVO eduAddressTreeVO = Convert.convert(EduAddressTreeVO.class, eduAddressTree);
                    eduAddressTreeVO.setChangeType(DataSaveEnum.insert);
                    TreeChangeHelper.addTreeChange(eduAddressTreeVO);
                }
                addressTreeVO.setAddressId(eduAddressTree.getAddressId());
                parentId = eduAddressTree.getAddressId();
            }
        }
        addressVO.setAddressId(parentId);
        return parentId;
    }


    private void updateEsStatus(Long id, Throwable e) {
        String esMsg;
        if (e.getMessage() != null) {
            esMsg = e.getMessage().length() < 200 ? e.getMessage() : e.getMessage().substring(0, 200);
        } else {
            esMsg = e.getClass().getName();
        }
        updateEsStatus(id, 2, esMsg);
    }


    private void updateEsStatus(Long id, Integer status, String msg) {
        if (id == null) {
            return;
        }
        try {
            UpdateWrapper<EduAddressTree> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().eq(EduAddressTree::getAddressId, id)
                    .set(EduAddressTree::getEsStatus, status)
                    .set(EduAddressTree::getEsMsg, msg)
                    .set(EduAddressTree::getEsTime, new Date());
            eduAddressTreeService.update(updateWrapper);
        } catch (Exception e) {
            log.error("更新es保存状态失败" + e.getMessage());
        }

    }

    /**
     * 链式进行保存学校数据
     *
     * @param eduAddressTree
     * @param collect        要批量保存的学校 需要批量改变tree_id  还需要清除id  由MybatisPlusi添加成功id补全导致的错误
     * @param schoolIds      关联的学校id集合
     */
    private void cascadeSaveByTreeId(EduAddressTree eduAddressTree, List<EduAddressTreeSchool> collect, List<String> schoolIds) {
        EduAddressTree addressTree = eduAddressTreeService.getById(eduAddressTree.getParentId());
        if (ObjectUtils.isEmpty(addressTree)) {
            throw new AppException("节点信息不存在，请联系管理员！");
        }
        //从数据库中查询 排除已经被关联的节点信息  schoolId， treeId
        QueryWrapper<EduAddressTreeSchool> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(EduAddressTreeSchool::getTreeId, addressTree.getAddressId())
                .in(EduAddressTreeSchool::getSchoolId, schoolIds);
        List<EduAddressTreeSchool> list = eduAddressTreeSchoolService.list(queryWrapper);

        //定义真正保存的学校
        List<EduAddressTreeSchool> objectList = collect;

        //然后判断collect 是否都符合要添加的学校
        if (!ObjectUtils.isEmpty(list)) {
            List<String> ids = list.stream().map(EduAddressTreeSchool::getSchoolId).collect(Collectors.toList());

            //将要添加的学校中的数据   已经关联的进行排除
            objectList = collect.stream().filter(item -> {
                return !ids.contains(item.getSchoolId());
            }).collect(Collectors.toList());

            if (objectList.size() == 0) {
                cascadeSaveByTreeId(addressTree, collect, schoolIds);
                return;
            }
        }
        //链式向上保存，存到区县  district  以下
        ArrayList<String> areaTypeList = new ArrayList<>();
        areaTypeList.add("country");
        areaTypeList.add("province");
        areaTypeList.add("city");
        areaTypeList.add("district");
        if (!areaTypeList.contains(addressTree.getAreaType())) {
            //需要重置collect中的treeId
            objectList = objectList.stream().peek(item -> {
                item.setId(null);
                item.setTreeId(addressTree.getAddressId());
            }).collect(Collectors.toList());
            eduAddressTreeSchoolService.saveBatch(objectList);
            //递归调用，向上查询  元素应该为 最开始的集合
            cascadeSaveByTreeId(addressTree, collect, schoolIds);
        }
    }


}
